Compare commits

...

8 Commits
0.13.5 ... main

Author SHA1 Message Date
Nix
2ca02b378c
Update README.md 2025-10-02 19:37:24 -03:00
Nix
3c9634bd24
added universall linux install 2025-10-02 19:34:56 -03:00
Nix
13a167f7fa
problems with zucaro/picomc
Some checks failed
Version Change Action / version-release (push) Has been cancelled
2025-09-26 10:11:59 -03:00
Nix
42dfaf8904
Update version.json 2025-09-26 10:09:20 -03:00
Nix
078518e5ad
hotfix 2025-09-26 10:08:58 -03:00
Nix
7f0108221b
Update README.md 2025-09-21 15:05:59 -03:00
Nix
16936e3a0d
Update PKGBUILD 2025-09-21 15:04:02 -03:00
Nix
8f24903fb3
Zucaro replace (#15)
Some checks failed
Version Change Action / version-release (push) Has been cancelled
* Initial commit of the replace

* Delete .github/workflows/Bleeding-Job.yaml

* remove picomc

* Update README.md

* Update version.json
2025-09-15 00:55:06 -03:00
10 changed files with 395 additions and 125 deletions

View File

@ -1,36 +0,0 @@
name: Bleeding Update version
on:
push:
branches:
- main
jobs:
update-version:
runs-on: ubuntu-latest
steps:
- name: Check out the repository
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Update version.json
run: |
git fetch --prune --unshallow
commit_count=$(git rev-list --count HEAD)
version=$(jq -r '.version' version.json)
jq --arg versionBleeding "$version-$commit_count" '. + {versionBleeding: $versionBleeding}' version.json > version.tmp && mv version.tmp version.json
- name: Commit and push changes
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git add version.json
git commit -m "Update version.json with commit count"
git push
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,11 +1,11 @@
pkgname=picodulce
pkgver=0.11.7
pkgver=0.13.5
pkgrel=1
pkgdesc="Launcher for Minecraft based on the picomc library"
pkgdesc="Launcher for Minecraft based on the zucaro library"
arch=('x86_64')
OPTIONS=(!strip !docs libtool emptydirs)
url="https://github.com/nixietab/picodulce"
license=('MIT') # Replace with your project's license
license=('MIT')
depends=('python' 'python-virtualenv' 'xdg-utils')
makedepends=('git')
source=("git+https://github.com/nixietab/picodulce.git")

View File

@ -27,7 +27,7 @@
</p>
Picodulce is a feature-rich launcher for Minecraft, developed using Qt5. It serves as a graphical user interface (GUI) for the picomc project, providing users with a seamless experience in managing and launching game versions.
Picodulce is a feature-rich launcher for Minecraft, developed using Qt5. It serves as a graphical user interface (GUI) for the [zucaro backend](https://github.com/nixietab/zucaro), providing users with a seamless experience in managing and launching game versions.
![imagen](https://github.com/user-attachments/assets/115b39be-47d3-4ac7-893a-5849c1e4570c)
@ -44,12 +44,19 @@
## Windows
For Windows systems using the [installer](https://github.com/nixietab/picodulce/releases/latest) is recommended
# Linux (Generic)
We have a install script, to use it run:
~~~
curl -sSL https://raw.githubusercontent.com/nixietab/picodulce/refs/heads/main/install-universal.sh | bash
~~~
## Arch Linux
The package is available in the [AUR](https://aur.archlinux.org/packages/picodulce) as ```picodulce```
For installing on Arch without using an AUR helper a PKGBUILD is provided
```
git clone https://aur.archlinux.org/picodulce.git
git clone https://github.com/nixietab/picodulce.git
cd picodulce
makepkg -si
```
@ -61,7 +68,7 @@ makepkg -si
``` git clone https://github.com/nixietab/picodulce ```
### 2. (Optional) Set Up a Virtual Environment
Setting up a virtual environment is recommended to avoid dependency conflicts. Picodulce relies on the path of the `picomc` project, and using a virtual environment helps prevent errors.
Setting up a virtual environment is recommended to avoid dependency conflicts. Picodulce relies on the path of the `zucaro` project, and using a virtual environment helps prevent errors.
Create the virtual environment:
@ -86,7 +93,7 @@ On the venv run it as a normal python script
```python picodulce.py```
Just make sure you have Java installed for running the actual game
Just make sure you have Java installed for running the actual game, or check the "manage java" option inside the launcher settings
### About the name
The name "Picodulce" comes from a popular Argentinian candy. This reflects the enjoyable and user-friendly experience that the launcher aims to provide, making game management straightforward and pleasant.

View File

@ -10,8 +10,8 @@ from PyQt5.QtWidgets import (QApplication, QDialog, QLabel, QVBoxLayout,
QPushButton, QLineEdit, QMessageBox)
from PyQt5.QtCore import QThread, pyqtSignal, Qt, QUrl, QObject
from PyQt5.QtGui import QDesktopServices
from picomc.logging import logger
from picomc.launcher import get_default_root, Launcher
from zucaro.logging import logger
from zucaro.launcher import get_default_root, Launcher
# Constants for Microsoft Authentication
URL_DEVICE_AUTH = "https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode"

View File

@ -1,6 +1,54 @@
import os
import json
import requests
import shutil
import modulecli
from PyQt5.QtWidgets import QApplication, QDialog, QVBoxLayout, QLabel, QProgressBar
from PyQt5.QtCore import Qt, QThread, pyqtSignal
import sys
class CopyThread(QThread):
progress_changed = pyqtSignal(int)
finished = pyqtSignal()
def __init__(self, src_dir, dst_dir):
super().__init__()
self.src_dir = src_dir
self.dst_dir = dst_dir
def run(self):
# Gather all files recursively
files_to_copy = []
for root, dirs, files in os.walk(self.src_dir):
for f in files:
full_path = os.path.join(root, f)
relative_path = os.path.relpath(full_path, self.src_dir)
files_to_copy.append(relative_path)
total_files = len(files_to_copy)
copied_files = 0
for relative_path in files_to_copy:
src_path = os.path.join(self.src_dir, relative_path)
dst_path = os.path.join(self.dst_dir, relative_path)
dst_folder = os.path.dirname(dst_path)
if not os.path.exists(dst_folder):
try:
os.makedirs(dst_folder)
except PermissionError:
print(f"Skipping folder {dst_folder} (permission denied)")
continue
try:
shutil.copy2(src_path, dst_path)
except PermissionError:
print(f"Skipping file {dst_path} (permission denied)")
copied_files += 1
progress_percent = int((copied_files / total_files) * 100)
self.progress_changed.emit(progress_percent)
self.finished.emit()
class HealthCheck:
@ -22,47 +70,112 @@ class HealthCheck:
"ThemeRepository": "https://raw.githubusercontent.com/nixietab/picodulce-themes/main/repo.json",
"Locale": "en",
"ManageJava": False,
"MaxRAM": 2,
"JavaPath": ""
"MaxRAM": "2G",
"JavaPath": "",
"ZucaroCheck": False,
}
# 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
if key not in self.config:
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 get_folder_size(self, folder_path):
total_size = 0
for dirpath, dirnames, filenames in os.walk(folder_path):
for f in filenames:
fp = os.path.join(dirpath, f)
if os.path.isfile(fp):
total_size += os.path.getsize(fp)
return total_size
def zucaro_health_check(self):
if self.config.get("ZucaroCheck"):
return
output = modulecli.run_command("instance dir").strip()
instance_dir = os.path.abspath(output)
base_dir = os.path.abspath(os.path.join(instance_dir, "..", ".."))
possible_zucaro = [os.path.join(base_dir, "zucaro"), os.path.join(base_dir, ".zucaro")]
possible_picomc = [os.path.join(base_dir, "picomc"), os.path.join(base_dir, ".picomc")]
zucaro_dir = next((d for d in possible_zucaro if os.path.exists(d)), None)
picomc_dir = next((d for d in possible_picomc if os.path.exists(d)), None)
if picomc_dir is None or zucaro_dir is None:
print("Required directories not found. Skipping copy.")
# Mark the check as done so it wont run again
self.config["ZucaroCheck"] = True
with open("config.json", "w") as f:
json.dump(self.config, f, indent=4)
return
picomc_size = self.get_folder_size(picomc_dir)
zucaro_size = self.get_folder_size(zucaro_dir)
if picomc_size <= zucaro_size:
print("No action needed. Zucaro folder is not smaller than Picomc.")
# Update config so the check is considered done
self.config["ZucaroCheck"] = True
with open("config.json", "w") as f:
json.dump(self.config, f, indent=4)
return
print(f"Copying Picomc ({picomc_size} bytes) to Zucaro ({zucaro_size} bytes)...")
app = QApplication.instance() or QApplication(sys.argv)
dialog = QDialog()
dialog.setWindowTitle("Working...")
dialog.setWindowModality(Qt.ApplicationModal)
layout = QVBoxLayout()
label = QLabel("Working on stuff, please wait...")
progress = QProgressBar()
progress.setValue(0)
layout.addWidget(label)
layout.addWidget(progress)
dialog.setLayout(layout)
# Setup copy thread
thread = CopyThread(picomc_dir, zucaro_dir)
thread.progress_changed.connect(progress.setValue)
thread.finished.connect(dialog.accept)
thread.start()
dialog.exec_() # Runs the modal event loop
# Mark as done
self.config["ZucaroCheck"] = True
with open("config.json", "w") as f:
json.dump(self.config, f, indent=4)
print("Copy completed.")
def themes_integrity(self):
# Define folder and file paths
themes_folder = "themes"
dark_theme_file = os.path.join(themes_folder, "Dark.json")
native_theme_file = os.path.join(themes_folder, "Native.json")
# Define the default content for Dark.json
dark_theme_content = {
"manifest": {
"name": "Dark",
@ -88,7 +201,6 @@ class HealthCheck:
"background_image_base64": ""
}
# Define the default content for Native.json
native_theme_content = {
"manifest": {
"name": "Native",
@ -99,25 +211,21 @@ class HealthCheck:
"palette": {}
}
# Step 1: Ensure the themes folder exists
if not os.path.exists(themes_folder):
print(f"Creating folder: {themes_folder}")
os.makedirs(themes_folder)
# Step 2: Ensure Dark.json exists
if not os.path.isfile(dark_theme_file):
print(f"Creating file: {dark_theme_file}")
with open(dark_theme_file, "w", encoding="utf-8") as file:
json.dump(dark_theme_content, file, indent=2)
print("Dark.json has been created successfully.")
# Step 3: Ensure Native.json exists
if not os.path.isfile(native_theme_file):
print(f"Creating file: {native_theme_file}")
with open(native_theme_file, "w", encoding="utf-8") as file:
json.dump(native_theme_content, file, indent=2)
print("Native.json has been created successfully.")
# Check if both files exist and print OK message
if os.path.isfile(dark_theme_file) and os.path.isfile(native_theme_file):
print("Theme Integrity OK")

110
install-universal.sh Normal file
View File

@ -0,0 +1,110 @@
#!/bin/bash
set -e
PICODULCE_DIR="$HOME/.picodulce"
GIT_URL="https://github.com/nixietab/picodulce.git"
DESKTOP_FILE="$HOME/.local/share/applications/picodulce.desktop"
BIN_FILE="/usr/bin/picodulce"
# --- Helper functions ---
msg() {
echo -e "\033[1;32m$1\033[0m"
}
err() {
echo -e "\033[1;31m$1\033[0m" >&2
exit 1
}
pause() {
read -rp "Press Enter to continue..."
}
# --- Check dependencies ---
msg "Checking Python3..."
if ! command -v python3 >/dev/null; then
err "Python3 is not installed. Please install it first."
fi
msg "Checking venv module..."
if ! python3 -m venv --help >/dev/null 2>&1; then
err "python3-venv is not available. Please install it."
fi
# --- Clone repo ---
msg "Cloning Picodulce repo..."
rm -rf "$PICODULCE_DIR"
git clone "$GIT_URL" "$PICODULCE_DIR"
# --- Create virtual environment ---
cd "$PICODULCE_DIR"
msg "Creating virtual environment..."
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# --- Create run.sh ---
msg "Creating run.sh..."
cat > "$PICODULCE_DIR/run.sh" <<'EOF'
#!/bin/bash
cd "$(dirname "$0")"
if [ ! -d "venv" ]; then
echo "venv folder does not exist. Creating virtual environment..."
python3 -m venv venv
source venv/bin/activate
echo "Installing required packages..."
pip install -r requirements.txt
else
source venv/bin/activate
fi
exec python picodulce.py
EOF
chmod +x "$PICODULCE_DIR/run.sh"
# --- Create .desktop entry ---
msg "Creating .desktop entry..."
mkdir -p "$(dirname "$DESKTOP_FILE")"
cat > "$DESKTOP_FILE" <<EOF
[Desktop Entry]
Name=Picodulce
Exec=$PICODULCE_DIR/run.sh
Icon=$PICODULCE_DIR/launcher_icon.ico
Terminal=true
Type=Application
Comment=Picodulce Launcher
Categories=Game;
EOF
# --- Ask if install in /usr/bin ---
echo
read -rp "Do you want to install the "picodulce" command? it requires sudo. (y/n) " choice
if [[ "$choice" =~ ^[Yy]$ ]]; then
if [ "$(id -u)" -ne 0 ]; then
echo "Root permissions required to install into /usr/bin"
sudo bash -c "cat > $BIN_FILE" <<EOF
#!/bin/bash
cd $PICODULCE_DIR
exec ./run.sh
EOF
sudo chmod +x "$BIN_FILE"
else
cat > "$BIN_FILE" <<EOF
#!/bin/bash
cd $PICODULCE_DIR
exec ./run.sh
EOF
chmod +x "$BIN_FILE"
fi
msg "Installed 'picodulce' command in /usr/bin"
fi
msg "Installation complete!"
echo "You can run Picodulce with:"
echo " $PICODULCE_DIR/run.sh"
echo "Or from your applications menu."

View File

@ -1,16 +1,27 @@
import click
from picomc.cli.main import picomc_cli
from io import StringIO
import sys
import shlex
import gc
def run_command(command="zucaro"):
# Remove all zucaro-related modules from sys.modules BEFORE import
modules_to_remove = [mod for mod in sys.modules if mod.startswith('zucaro')]
for mod in modules_to_remove:
del sys.modules[mod]
gc.collect()
# Import zucaro_cli dynamically
from zucaro.cli.main import zucaro_cli
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())
# Use shlex.split to properly parse the command string
# This will call Click's CLI as if from command line, using args
zucaro_cli.main(args=shlex.split(command))
except SystemExit as e:
if e.code != 0:
print(f"Command exited with code {e.code}", file=sys.stderr)
@ -23,8 +34,13 @@ def run_command(command="picomc"):
output = mystdout.getvalue().strip()
error = mystderr.getvalue().strip()
# Cleanup: remove zucaro-related modules from sys.modules and force garbage collection
modules_to_remove = [mod for mod in sys.modules if mod.startswith('zucaro')]
for mod in modules_to_remove:
del sys.modules[mod]
gc.collect()
if not output:
return f"Error: No output from command. Stderr: {error}"
return output

View File

@ -15,14 +15,14 @@ from authser import MinecraftAuthenticator
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, QSpinBox, QFileDialog, 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 datetime import datetime
logging.basicConfig(level=logging.ERROR, format='%(levelname)s - %(message)s')
class PicomcVersionSelector(QWidget):
class zucaroVersionSelector(QWidget):
def __init__(self):
self.current_state = "menu"
self.open_dialogs = []
@ -31,6 +31,7 @@ class PicomcVersionSelector(QWidget):
health_checker = HealthCheck()
health_checker.themes_integrity()
health_checker.check_config_file()
health_checker.zucaro_health_check()
self.config = health_checker.config
themes_folder = "themes"
@ -126,7 +127,7 @@ class PicomcVersionSelector(QWidget):
def FirstLaunch(self):
try:
self.config_path = "config.json"
print("Running picomc instance create default command...")
print("Running zucaro instance create default command...")
# Run the command using modulecli
command = "instance create default"
@ -296,21 +297,17 @@ class PicomcVersionSelector(QWidget):
def open_settings_dialog(self):
dialog = QDialog(self)
dialog.setWindowTitle('Settings')
# Make the window resizable
dialog.setMinimumSize(400, 300)
# Create a Tab Widget
tab_widget = QTabWidget()
# Create the Settings Tab
# --- Settings Tab ---
settings_tab = QWidget()
settings_layout = QVBoxLayout()
title_label = QLabel('Settings')
title_label.setFont(QFont("Arial", 14))
# Create checkboxes for settings tab
discord_rcp_checkbox = QCheckBox('Discord Rich Presence')
discord_rcp_checkbox.setChecked(self.config.get("IsRCPenabled", False))
@ -326,7 +323,6 @@ class PicomcVersionSelector(QWidget):
settings_layout.addWidget(check_updates_checkbox)
settings_layout.addWidget(bleeding_edge_checkbox)
# Add buttons in the settings tab
update_button = QPushButton('Check for updates')
update_button.clicked.connect(self.check_for_update)
@ -342,53 +338,99 @@ class PicomcVersionSelector(QWidget):
settings_tab.setLayout(settings_layout)
# Create the Customization Tab
# --- Customization Tab ---
customization_tab = QWidget()
customization_layout = QVBoxLayout()
# Create theme background checkbox for customization tab
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(f"Current Theme: {theme_filename}")
# QListWidget to display available themes
json_files_label = QLabel('Installed Themes:')
self.json_files_list_widget = QListWidget()
# Track selected theme
self.selected_theme = theme_filename # Default to current theme
# Build the list of themes
self.selected_theme = theme_filename
themes_list = self.build_themes_list()
# Populate themes initially
self.populate_themes(self.json_files_list_widget, themes_list)
# Update current theme label when a theme is selected
self.json_files_list_widget.itemClicked.connect(
lambda: self.on_theme_selected(self.json_files_list_widget, current_theme_label)
)
# Add widgets to the layout
customization_layout.addWidget(theme_background_checkbox)
customization_layout.addWidget(current_theme_label)
customization_layout.addWidget(json_files_label)
customization_layout.addWidget(self.json_files_list_widget)
# Button to download themes
download_themes_button = QPushButton("Download More Themes")
download_themes_button.clicked.connect(self.download_themes_window)
customization_layout.addWidget(download_themes_button)
customization_tab.setLayout(customization_layout)
# Add the tabs to the TabWidget
# --- Java Tab ---
java_tab = QWidget()
java_layout = QVBoxLayout()
# Java path input with browse button
java_path_layout = QHBoxLayout()
java_path_input = QLineEdit()
java_path_input.setPlaceholderText("Custom Java Installation Path")
java_path_input.setText(self.config.get("JavaPath", ""))
browse_button = QPushButton("Examine")
browse_button.clicked.connect(lambda: self.browse_java_path(java_path_input))
java_path_layout.addWidget(java_path_input)
java_path_layout.addWidget(browse_button)
ram_layout = QHBoxLayout()
ram_label = QLabel("Assigned RAM:")
ram_selector = QLineEdit()
ram_selector.setPlaceholderText("2G") # Show default placeholder
# RAM selector
ram_layout = QHBoxLayout()
ram_label = QLabel("Assigned RAM:")
ram_selector = QLineEdit()
ram_selector.setPlaceholderText("2G") # Show default placeholder
# Set initial value from config, ensuring it ends with 'G'
initial_ram = self.config.get("MaxRAM", "2G")
if not initial_ram.endswith('G'):
initial_ram += 'G'
ram_selector.setText(initial_ram)
# Ensure 'G' is always present when focus is lost
def ensure_g_suffix():
current_text = ram_selector.text()
if not current_text.endswith('G'):
ram_selector.setText(current_text + 'G')
ram_selector.editingFinished.connect(ensure_g_suffix)
ram_layout.addWidget(ram_label)
ram_layout.addWidget(ram_selector)
# Manage Java checkbox
manage_java_checkbox = QCheckBox("Manage Java")
manage_java_checkbox.setChecked(self.config.get("ManageJava", False))
manage_java_info = QLabel(
"<b>Disclaimer:</b> Experimental feature. Do not change these settings "
"unless you are sure of what you are doing. "
" If Manage Java is enabledthe launcher will download Java binaries for your OS only for Minecraft compatibility purposes.")
manage_java_info.setWordWrap(True)
# Add to layout
java_layout.addLayout(java_path_layout)
java_layout.addLayout(ram_layout)
java_layout.addWidget(manage_java_checkbox)
java_layout.addWidget(manage_java_info)
java_tab.setLayout(java_layout)
# Add all tabs
tab_widget.addTab(settings_tab, "Settings")
tab_widget.addTab(customization_tab, "Customization")
tab_widget.addTab(java_tab, "Java")
# Save button
save_button = QPushButton('Save')
@ -398,11 +440,13 @@ class PicomcVersionSelector(QWidget):
check_updates_checkbox.isChecked(),
theme_background_checkbox.isChecked(),
self.selected_theme,
bleeding_edge_checkbox.isChecked()
bleeding_edge_checkbox.isChecked(),
java_path_input.text(),
ram_selector.text(),
manage_java_checkbox.isChecked()
)
)
# Main layout
main_layout = QVBoxLayout()
main_layout.addWidget(tab_widget)
main_layout.addWidget(save_button)
@ -410,6 +454,13 @@ class PicomcVersionSelector(QWidget):
dialog.setLayout(main_layout)
dialog.exec_()
def browse_java_path(self, java_path_input):
path, _ = QFileDialog.getOpenFileName(self, "Select Java Executable")
if path:
java_path_input.setText(path)
def show_bleeding_edge_popup(self, checkbox):
if checkbox.isChecked():
response = QMessageBox.question(
@ -638,20 +689,31 @@ class PicomcVersionSelector(QWidget):
## REPOSITORY BLOCK ENDS
def save_settings(self, is_rcp_enabled, check_updates_on_start, theme_background, selected_theme, is_bleeding):
def save_settings(
self,
is_rcp_enabled,
check_updates_on_start,
theme_background,
selected_theme,
is_bleeding,
java_path,
ram_allocation,
manage_java_enabled
):
config_path = "config.json"
updated_config = {
"IsRCPenabled": is_rcp_enabled,
"CheckUpdate": check_updates_on_start,
"ThemeBackground": theme_background,
"Theme": selected_theme,
"IsBleeding": is_bleeding
"IsBleeding": is_bleeding,
"ManageJava": manage_java_enabled,
"MaxRAM": ram_allocation,
"JavaPath": java_path,
}
# Update config values
self.config.update(updated_config)
# Save updated config to file
with open(config_path, "w") as config_file:
json.dump(self.config, config_file, indent=4)
@ -709,7 +771,7 @@ class PicomcVersionSelector(QWidget):
# Open the directory in the system's file explorer
QDesktopServices.openUrl(QUrl.fromLocalFile(game_directory))
except Exception as e:
print(f"Error running picomc command: {e}")
print(f"Error running zucaro command: {e}")
def populate_installed_versions(self):
config_path = "config.json"
@ -737,7 +799,7 @@ class PicomcVersionSelector(QWidget):
if not output:
raise Exception("Failed to get output from modulecli")
except Exception as e:
logging.error("Error running 'picomc': %s", e)
logging.error("Error running 'zucaro': %s", e)
return
# Parse the output and replace '[local]' with a space
@ -756,7 +818,7 @@ class PicomcVersionSelector(QWidget):
self.installed_version_combo.addItems(versions)
def populate_installed_versions_normal_order(self):
# Run the 'picomc instance create default' command at the start
# Run the 'zucaro instance create default' command at the start
try:
command = "instance create default"
output = modulecli.run_command(command)
@ -766,7 +828,7 @@ class PicomcVersionSelector(QWidget):
logging.error("Error creating default instance: %s", str(e))
return
# Run the 'picomc version list' command and get the output
# Run the 'zucaro version list' command and get the output
try:
command = "version list"
output = modulecli.run_command(command)
@ -830,40 +892,44 @@ class PicomcVersionSelector(QWidget):
def run_game(self, selected_instance):
try:
# Set current_state to the selected instance
self.current_state = selected_instance
self.start_time = time.time()
# Read the config.json to get the "Instance" value
# Read config
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
instance_value = config.get("Instance", "default")
max_ram = config.get("MaxRAM", 2)
manage_java = config.get("ManageJava", False)
java_path = config.get("JavaPath", "")
# Update lastplayed field in config.json on a separate thread
# Update last played on a thread
update_thread = threading.Thread(target=self.update_last_played, args=(selected_instance,))
update_thread.start()
# Run the game using the modulecli module
command = f"instance launch --version-override {selected_instance} {instance_value}"
# Build command
command = f"instance launch {instance_value} --version-override {selected_instance} --assigned-ram {max_ram}"
if manage_java:
command += " --manage-java"
if java_path:
command += f" --java {java_path}"
print(f"Launching command: {command}")
output = modulecli.run_command(command)
print(f"modulecli output: {output}")
if not output:
raise Exception("Failed to get output from modulecli")
except Exception as e:
error_message = f"Error playing {selected_instance}: {e}"
print(error_message) # Add this for debugging
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)
)
# (Show error in UI if necessary)
finally:
# Reset current_state to "menu" after the game closes
self.current_state = "menu"
self.update_total_playtime(self.start_time)
def update_last_played(self, selected_instance):
config_path = "config.json"
self.config["LastPlayed"] = selected_instance
@ -1137,7 +1203,7 @@ class PicomcVersionSelector(QWidget):
about_message = (
f"PicoDulce Launcher (v{version_number})\n\n"
"A simple Minecraft launcher built using Qt, based on the picomc project.\n\n"
"A simple Minecraft launcher built using Qt, based on the zucaro backend.\n\n"
"Credits:\n"
"Nixietab: Code and UI design\n"
"Wabaano: Graphic design\n"
@ -1425,7 +1491,7 @@ class ModLoaderAndVersionMenu(QDialog):
if instance_name:
try:
# Run the "picomc instance create" command
# Run the "zucaro instance create" command
command = f"instance create {instance_name}"
modulecli.run_command(command)
@ -1450,7 +1516,7 @@ class ModLoaderAndVersionMenu(QDialog):
return
try:
# Run the "picomc instance rename" command
# Run the "zucaro instance rename" command
command = f"instance rename {old_instance_name} {new_instance_name}"
modulecli.run_command(command)
@ -1480,7 +1546,7 @@ class ModLoaderAndVersionMenu(QDialog):
if confirm_delete == QMessageBox.Yes:
try:
# Run the "picomc instance delete" command
# Run the "zucaro instance delete" command
command = f"instance delete {instance_name}"
modulecli.run_command(command)
@ -1496,7 +1562,7 @@ class ModLoaderAndVersionMenu(QDialog):
def load_instances(self):
try:
# Run the "picomc instance list" command
# Run the "zucaro instance list" command
command = "instance list"
output = modulecli.run_command(command)
@ -1792,6 +1858,6 @@ if __name__ == '__main__':
app.setWindowIcon(QIcon('holiday.ico')) # Set holiday icon
else:
app.setWindowIcon(QIcon('launcher_icon.ico')) # Set regular icon
window = PicomcVersionSelector()
window = zucaroVersionSelector()
window.show()
sys.exit(app.exec_())

View File

@ -1,4 +1,3 @@
picomc
zucaro
PyQt5
requests

View File

@ -1,5 +1,5 @@
{
"version": "0.13.3",
"version": "0.13.6",
"links": [
"https://raw.githubusercontent.com/nixietab/picodulce/main/version.json",
"https://raw.githubusercontent.com/nixietab/picodulce/main/picodulce.py",