mirror of
https://github.com/nixietab/picodulce.git
synced 2025-11-01 14:05:10 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ca02b378c | ||
|
|
3c9634bd24 | ||
|
|
13a167f7fa | ||
|
|
42dfaf8904 | ||
|
|
078518e5ad | ||
|
|
7f0108221b | ||
|
|
16936e3a0d | ||
|
|
8f24903fb3 |
36
.github/workflows/Bleeding-Job.yaml
vendored
36
.github/workflows/Bleeding-Job.yaml
vendored
@ -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 }}
|
|
||||||
6
PKGBUILD
6
PKGBUILD
@ -1,11 +1,11 @@
|
|||||||
pkgname=picodulce
|
pkgname=picodulce
|
||||||
pkgver=0.11.7
|
pkgver=0.13.5
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Launcher for Minecraft based on the picomc library"
|
pkgdesc="Launcher for Minecraft based on the zucaro library"
|
||||||
arch=('x86_64')
|
arch=('x86_64')
|
||||||
OPTIONS=(!strip !docs libtool emptydirs)
|
OPTIONS=(!strip !docs libtool emptydirs)
|
||||||
url="https://github.com/nixietab/picodulce"
|
url="https://github.com/nixietab/picodulce"
|
||||||
license=('MIT') # Replace with your project's license
|
license=('MIT')
|
||||||
depends=('python' 'python-virtualenv' 'xdg-utils')
|
depends=('python' 'python-virtualenv' 'xdg-utils')
|
||||||
makedepends=('git')
|
makedepends=('git')
|
||||||
source=("git+https://github.com/nixietab/picodulce.git")
|
source=("git+https://github.com/nixietab/picodulce.git")
|
||||||
|
|||||||
15
README.md
15
README.md
@ -27,7 +27,7 @@
|
|||||||
</p>
|
</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.
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
@ -44,12 +44,19 @@
|
|||||||
## Windows
|
## Windows
|
||||||
For Windows systems using the [installer](https://github.com/nixietab/picodulce/releases/latest) is recommended
|
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
|
## Arch Linux
|
||||||
The package is available in the [AUR](https://aur.archlinux.org/packages/picodulce) as ```picodulce```
|
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
|
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
|
cd picodulce
|
||||||
makepkg -si
|
makepkg -si
|
||||||
```
|
```
|
||||||
@ -61,7 +68,7 @@ makepkg -si
|
|||||||
``` git clone https://github.com/nixietab/picodulce ```
|
``` git clone https://github.com/nixietab/picodulce ```
|
||||||
|
|
||||||
### 2. (Optional) Set Up a Virtual Environment
|
### 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:
|
Create the virtual environment:
|
||||||
|
|
||||||
@ -86,7 +93,7 @@ On the venv run it as a normal python script
|
|||||||
|
|
||||||
```python picodulce.py```
|
```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
|
### 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.
|
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.
|
||||||
|
|||||||
@ -10,8 +10,8 @@ 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
|
||||||
from PyQt5.QtGui import QDesktopServices
|
from PyQt5.QtGui import QDesktopServices
|
||||||
from picomc.logging import logger
|
from zucaro.logging import logger
|
||||||
from picomc.launcher import get_default_root, Launcher
|
from zucaro.launcher import get_default_root, Launcher
|
||||||
|
|
||||||
# Constants for Microsoft Authentication
|
# Constants for Microsoft Authentication
|
||||||
URL_DEVICE_AUTH = "https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode"
|
URL_DEVICE_AUTH = "https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode"
|
||||||
|
|||||||
140
healthcheck.py
140
healthcheck.py
@ -1,6 +1,54 @@
|
|||||||
import os
|
import os
|
||||||
import json
|
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:
|
class HealthCheck:
|
||||||
@ -22,47 +70,112 @@ class HealthCheck:
|
|||||||
"ThemeRepository": "https://raw.githubusercontent.com/nixietab/picodulce-themes/main/repo.json",
|
"ThemeRepository": "https://raw.githubusercontent.com/nixietab/picodulce-themes/main/repo.json",
|
||||||
"Locale": "en",
|
"Locale": "en",
|
||||||
"ManageJava": False,
|
"ManageJava": False,
|
||||||
"MaxRAM": 2,
|
"MaxRAM": "2G",
|
||||||
"JavaPath": ""
|
"JavaPath": "",
|
||||||
|
"ZucaroCheck": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Step 1: Check if the file exists; if not, create it with default values
|
|
||||||
if not os.path.exists(config_path):
|
if not os.path.exists(config_path):
|
||||||
with open(config_path, "w") as config_file:
|
with open(config_path, "w") as config_file:
|
||||||
json.dump(default_config, config_file, indent=4)
|
json.dump(default_config, config_file, indent=4)
|
||||||
self.config = default_config
|
self.config = default_config
|
||||||
return
|
return
|
||||||
|
|
||||||
# Step 2: Try loading the config file, handle invalid JSON
|
|
||||||
try:
|
try:
|
||||||
with open(config_path, "r") as config_file:
|
with open(config_path, "r") as config_file:
|
||||||
self.config = json.load(config_file)
|
self.config = json.load(config_file)
|
||||||
except (json.JSONDecodeError, ValueError):
|
except (json.JSONDecodeError, ValueError):
|
||||||
# File is corrupted, overwrite it with default configuration
|
|
||||||
with open(config_path, "w") as config_file:
|
with open(config_path, "w") as config_file:
|
||||||
json.dump(default_config, config_file, indent=4)
|
json.dump(default_config, config_file, indent=4)
|
||||||
self.config = default_config
|
self.config = default_config
|
||||||
return
|
return
|
||||||
|
|
||||||
# Step 3: Check for missing keys and add defaults if necessary
|
|
||||||
updated = False
|
updated = False
|
||||||
for key, value in default_config.items():
|
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
|
self.config[key] = value
|
||||||
updated = True
|
updated = True
|
||||||
|
|
||||||
# Step 4: Save the repaired config back to the file
|
|
||||||
if updated:
|
if updated:
|
||||||
with open(config_path, "w") as config_file:
|
with open(config_path, "w") as config_file:
|
||||||
json.dump(self.config, config_file, indent=4)
|
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):
|
def themes_integrity(self):
|
||||||
# Define folder and file paths
|
|
||||||
themes_folder = "themes"
|
themes_folder = "themes"
|
||||||
dark_theme_file = os.path.join(themes_folder, "Dark.json")
|
dark_theme_file = os.path.join(themes_folder, "Dark.json")
|
||||||
native_theme_file = os.path.join(themes_folder, "Native.json")
|
native_theme_file = os.path.join(themes_folder, "Native.json")
|
||||||
|
|
||||||
# Define the default content for Dark.json
|
|
||||||
dark_theme_content = {
|
dark_theme_content = {
|
||||||
"manifest": {
|
"manifest": {
|
||||||
"name": "Dark",
|
"name": "Dark",
|
||||||
@ -88,7 +201,6 @@ class HealthCheck:
|
|||||||
"background_image_base64": ""
|
"background_image_base64": ""
|
||||||
}
|
}
|
||||||
|
|
||||||
# Define the default content for Native.json
|
|
||||||
native_theme_content = {
|
native_theme_content = {
|
||||||
"manifest": {
|
"manifest": {
|
||||||
"name": "Native",
|
"name": "Native",
|
||||||
@ -99,25 +211,21 @@ class HealthCheck:
|
|||||||
"palette": {}
|
"palette": {}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Step 1: Ensure the themes folder exists
|
|
||||||
if not os.path.exists(themes_folder):
|
if not os.path.exists(themes_folder):
|
||||||
print(f"Creating folder: {themes_folder}")
|
print(f"Creating folder: {themes_folder}")
|
||||||
os.makedirs(themes_folder)
|
os.makedirs(themes_folder)
|
||||||
|
|
||||||
# Step 2: Ensure Dark.json exists
|
|
||||||
if not os.path.isfile(dark_theme_file):
|
if not os.path.isfile(dark_theme_file):
|
||||||
print(f"Creating file: {dark_theme_file}")
|
print(f"Creating file: {dark_theme_file}")
|
||||||
with open(dark_theme_file, "w", encoding="utf-8") as file:
|
with open(dark_theme_file, "w", encoding="utf-8") as file:
|
||||||
json.dump(dark_theme_content, file, indent=2)
|
json.dump(dark_theme_content, file, indent=2)
|
||||||
print("Dark.json has been created successfully.")
|
print("Dark.json has been created successfully.")
|
||||||
|
|
||||||
# Step 3: Ensure Native.json exists
|
|
||||||
if not os.path.isfile(native_theme_file):
|
if not os.path.isfile(native_theme_file):
|
||||||
print(f"Creating file: {native_theme_file}")
|
print(f"Creating file: {native_theme_file}")
|
||||||
with open(native_theme_file, "w", encoding="utf-8") as file:
|
with open(native_theme_file, "w", encoding="utf-8") as file:
|
||||||
json.dump(native_theme_content, file, indent=2)
|
json.dump(native_theme_content, file, indent=2)
|
||||||
print("Native.json has been created successfully.")
|
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):
|
if os.path.isfile(dark_theme_file) and os.path.isfile(native_theme_file):
|
||||||
print("Theme Integrity OK")
|
print("Theme Integrity OK")
|
||||||
|
|||||||
110
install-universal.sh
Normal file
110
install-universal.sh
Normal 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."
|
||||||
26
modulecli.py
26
modulecli.py
@ -1,16 +1,27 @@
|
|||||||
import click
|
|
||||||
from picomc.cli.main import picomc_cli
|
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
import sys
|
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
|
# Redirect stdout and stderr to capture the command output
|
||||||
old_stdout, old_stderr = sys.stdout, sys.stderr
|
old_stdout, old_stderr = sys.stdout, sys.stderr
|
||||||
sys.stdout = mystdout = StringIO()
|
sys.stdout = mystdout = StringIO()
|
||||||
sys.stderr = mystderr = StringIO()
|
sys.stderr = mystderr = StringIO()
|
||||||
|
|
||||||
try:
|
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:
|
except SystemExit as e:
|
||||||
if e.code != 0:
|
if e.code != 0:
|
||||||
print(f"Command exited with code {e.code}", file=sys.stderr)
|
print(f"Command exited with code {e.code}", file=sys.stderr)
|
||||||
@ -23,8 +34,13 @@ def run_command(command="picomc"):
|
|||||||
|
|
||||||
output = mystdout.getvalue().strip()
|
output = mystdout.getvalue().strip()
|
||||||
error = mystderr.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:
|
if not output:
|
||||||
return f"Error: No output from command. Stderr: {error}"
|
return f"Error: No output from command. Stderr: {error}"
|
||||||
|
|
||||||
return output
|
return output
|
||||||
180
picodulce.py
180
picodulce.py
@ -15,14 +15,14 @@ from authser import MinecraftAuthenticator
|
|||||||
from healthcheck import HealthCheck
|
from healthcheck import HealthCheck
|
||||||
import modulecli
|
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.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='%(levelname)s - %(message)s')
|
logging.basicConfig(level=logging.ERROR, format='%(levelname)s - %(message)s')
|
||||||
|
|
||||||
class PicomcVersionSelector(QWidget):
|
class zucaroVersionSelector(QWidget):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.current_state = "menu"
|
self.current_state = "menu"
|
||||||
self.open_dialogs = []
|
self.open_dialogs = []
|
||||||
@ -31,6 +31,7 @@ class PicomcVersionSelector(QWidget):
|
|||||||
health_checker = HealthCheck()
|
health_checker = HealthCheck()
|
||||||
health_checker.themes_integrity()
|
health_checker.themes_integrity()
|
||||||
health_checker.check_config_file()
|
health_checker.check_config_file()
|
||||||
|
health_checker.zucaro_health_check()
|
||||||
self.config = health_checker.config
|
self.config = health_checker.config
|
||||||
|
|
||||||
themes_folder = "themes"
|
themes_folder = "themes"
|
||||||
@ -126,7 +127,7 @@ class PicomcVersionSelector(QWidget):
|
|||||||
def FirstLaunch(self):
|
def FirstLaunch(self):
|
||||||
try:
|
try:
|
||||||
self.config_path = "config.json"
|
self.config_path = "config.json"
|
||||||
print("Running picomc instance create default command...")
|
print("Running zucaro instance create default command...")
|
||||||
|
|
||||||
# Run the command using modulecli
|
# Run the command using modulecli
|
||||||
command = "instance create default"
|
command = "instance create default"
|
||||||
@ -296,21 +297,17 @@ class PicomcVersionSelector(QWidget):
|
|||||||
def open_settings_dialog(self):
|
def open_settings_dialog(self):
|
||||||
dialog = QDialog(self)
|
dialog = QDialog(self)
|
||||||
dialog.setWindowTitle('Settings')
|
dialog.setWindowTitle('Settings')
|
||||||
|
|
||||||
# Make the window resizable
|
|
||||||
dialog.setMinimumSize(400, 300)
|
dialog.setMinimumSize(400, 300)
|
||||||
|
|
||||||
# Create a Tab Widget
|
|
||||||
tab_widget = QTabWidget()
|
tab_widget = QTabWidget()
|
||||||
|
|
||||||
# Create the Settings Tab
|
# --- Settings Tab ---
|
||||||
settings_tab = QWidget()
|
settings_tab = QWidget()
|
||||||
settings_layout = QVBoxLayout()
|
settings_layout = QVBoxLayout()
|
||||||
|
|
||||||
title_label = QLabel('Settings')
|
title_label = QLabel('Settings')
|
||||||
title_label.setFont(QFont("Arial", 14))
|
title_label.setFont(QFont("Arial", 14))
|
||||||
|
|
||||||
# Create checkboxes for settings tab
|
|
||||||
discord_rcp_checkbox = QCheckBox('Discord Rich Presence')
|
discord_rcp_checkbox = QCheckBox('Discord Rich Presence')
|
||||||
discord_rcp_checkbox.setChecked(self.config.get("IsRCPenabled", False))
|
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(check_updates_checkbox)
|
||||||
settings_layout.addWidget(bleeding_edge_checkbox)
|
settings_layout.addWidget(bleeding_edge_checkbox)
|
||||||
|
|
||||||
# Add buttons in the settings tab
|
|
||||||
update_button = QPushButton('Check for updates')
|
update_button = QPushButton('Check for updates')
|
||||||
update_button.clicked.connect(self.check_for_update)
|
update_button.clicked.connect(self.check_for_update)
|
||||||
|
|
||||||
@ -342,53 +338,99 @@ class PicomcVersionSelector(QWidget):
|
|||||||
|
|
||||||
settings_tab.setLayout(settings_layout)
|
settings_tab.setLayout(settings_layout)
|
||||||
|
|
||||||
# Create the Customization Tab
|
# --- Customization Tab ---
|
||||||
customization_tab = QWidget()
|
customization_tab = QWidget()
|
||||||
customization_layout = QVBoxLayout()
|
customization_layout = QVBoxLayout()
|
||||||
|
|
||||||
# Create theme background checkbox for customization tab
|
|
||||||
theme_background_checkbox = QCheckBox('Theme Background')
|
theme_background_checkbox = QCheckBox('Theme Background')
|
||||||
theme_background_checkbox.setChecked(self.config.get("ThemeBackground", False))
|
theme_background_checkbox.setChecked(self.config.get("ThemeBackground", False))
|
||||||
|
|
||||||
# Label to show currently selected theme
|
|
||||||
theme_filename = self.config.get('Theme', 'Dark.json')
|
theme_filename = self.config.get('Theme', 'Dark.json')
|
||||||
current_theme_label = QLabel(f"Current Theme: {theme_filename}")
|
current_theme_label = QLabel(f"Current Theme: {theme_filename}")
|
||||||
|
|
||||||
# QListWidget to display available themes
|
|
||||||
json_files_label = QLabel('Installed Themes:')
|
json_files_label = QLabel('Installed Themes:')
|
||||||
self.json_files_list_widget = QListWidget()
|
self.json_files_list_widget = QListWidget()
|
||||||
|
self.selected_theme = theme_filename
|
||||||
# Track selected theme
|
|
||||||
self.selected_theme = theme_filename # Default to current theme
|
|
||||||
|
|
||||||
# Build the list of themes
|
|
||||||
themes_list = self.build_themes_list()
|
themes_list = self.build_themes_list()
|
||||||
|
|
||||||
# Populate themes initially
|
|
||||||
self.populate_themes(self.json_files_list_widget, themes_list)
|
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(
|
self.json_files_list_widget.itemClicked.connect(
|
||||||
lambda: self.on_theme_selected(self.json_files_list_widget, current_theme_label)
|
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(theme_background_checkbox)
|
||||||
customization_layout.addWidget(current_theme_label)
|
customization_layout.addWidget(current_theme_label)
|
||||||
customization_layout.addWidget(json_files_label)
|
customization_layout.addWidget(json_files_label)
|
||||||
customization_layout.addWidget(self.json_files_list_widget)
|
customization_layout.addWidget(self.json_files_list_widget)
|
||||||
|
|
||||||
# Button to download themes
|
|
||||||
download_themes_button = QPushButton("Download More Themes")
|
download_themes_button = QPushButton("Download More Themes")
|
||||||
download_themes_button.clicked.connect(self.download_themes_window)
|
download_themes_button.clicked.connect(self.download_themes_window)
|
||||||
|
|
||||||
customization_layout.addWidget(download_themes_button)
|
customization_layout.addWidget(download_themes_button)
|
||||||
|
|
||||||
customization_tab.setLayout(customization_layout)
|
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(settings_tab, "Settings")
|
||||||
tab_widget.addTab(customization_tab, "Customization")
|
tab_widget.addTab(customization_tab, "Customization")
|
||||||
|
tab_widget.addTab(java_tab, "Java")
|
||||||
|
|
||||||
# Save button
|
# Save button
|
||||||
save_button = QPushButton('Save')
|
save_button = QPushButton('Save')
|
||||||
@ -398,11 +440,13 @@ class PicomcVersionSelector(QWidget):
|
|||||||
check_updates_checkbox.isChecked(),
|
check_updates_checkbox.isChecked(),
|
||||||
theme_background_checkbox.isChecked(),
|
theme_background_checkbox.isChecked(),
|
||||||
self.selected_theme,
|
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 = QVBoxLayout()
|
||||||
main_layout.addWidget(tab_widget)
|
main_layout.addWidget(tab_widget)
|
||||||
main_layout.addWidget(save_button)
|
main_layout.addWidget(save_button)
|
||||||
@ -410,6 +454,13 @@ class PicomcVersionSelector(QWidget):
|
|||||||
dialog.setLayout(main_layout)
|
dialog.setLayout(main_layout)
|
||||||
dialog.exec_()
|
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):
|
def show_bleeding_edge_popup(self, checkbox):
|
||||||
if checkbox.isChecked():
|
if checkbox.isChecked():
|
||||||
response = QMessageBox.question(
|
response = QMessageBox.question(
|
||||||
@ -638,20 +689,31 @@ class PicomcVersionSelector(QWidget):
|
|||||||
## REPOSITORY BLOCK ENDS
|
## 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"
|
config_path = "config.json"
|
||||||
updated_config = {
|
updated_config = {
|
||||||
"IsRCPenabled": is_rcp_enabled,
|
"IsRCPenabled": is_rcp_enabled,
|
||||||
"CheckUpdate": check_updates_on_start,
|
"CheckUpdate": check_updates_on_start,
|
||||||
"ThemeBackground": theme_background,
|
"ThemeBackground": theme_background,
|
||||||
"Theme": selected_theme,
|
"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)
|
self.config.update(updated_config)
|
||||||
|
|
||||||
# Save updated config to file
|
|
||||||
with open(config_path, "w") as config_file:
|
with open(config_path, "w") as config_file:
|
||||||
json.dump(self.config, config_file, indent=4)
|
json.dump(self.config, config_file, indent=4)
|
||||||
|
|
||||||
@ -709,7 +771,7 @@ class PicomcVersionSelector(QWidget):
|
|||||||
# 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 Exception as e:
|
except Exception as e:
|
||||||
print(f"Error running picomc command: {e}")
|
print(f"Error running zucaro command: {e}")
|
||||||
|
|
||||||
def populate_installed_versions(self):
|
def populate_installed_versions(self):
|
||||||
config_path = "config.json"
|
config_path = "config.json"
|
||||||
@ -737,7 +799,7 @@ class PicomcVersionSelector(QWidget):
|
|||||||
if not output:
|
if not output:
|
||||||
raise Exception("Failed to get output from modulecli")
|
raise Exception("Failed to get output from modulecli")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("Error running 'picomc': %s", e)
|
logging.error("Error running 'zucaro': %s", e)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Parse the output and replace '[local]' with a space
|
# Parse the output and replace '[local]' with a space
|
||||||
@ -756,7 +818,7 @@ class PicomcVersionSelector(QWidget):
|
|||||||
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 'zucaro instance create default' command at the start
|
||||||
try:
|
try:
|
||||||
command = "instance create default"
|
command = "instance create default"
|
||||||
output = modulecli.run_command(command)
|
output = modulecli.run_command(command)
|
||||||
@ -766,7 +828,7 @@ class PicomcVersionSelector(QWidget):
|
|||||||
logging.error("Error creating default instance: %s", str(e))
|
logging.error("Error creating default instance: %s", str(e))
|
||||||
return
|
return
|
||||||
|
|
||||||
# Run the 'picomc version list' command and get the output
|
# Run the 'zucaro version list' command and get the output
|
||||||
try:
|
try:
|
||||||
command = "version list"
|
command = "version list"
|
||||||
output = modulecli.run_command(command)
|
output = modulecli.run_command(command)
|
||||||
@ -830,40 +892,44 @@ class PicomcVersionSelector(QWidget):
|
|||||||
|
|
||||||
def run_game(self, selected_instance):
|
def run_game(self, selected_instance):
|
||||||
try:
|
try:
|
||||||
# Set current_state to the selected instance
|
|
||||||
self.current_state = selected_instance
|
self.current_state = selected_instance
|
||||||
self.start_time = time.time()
|
self.start_time = time.time()
|
||||||
|
|
||||||
# Read the config.json to get the "Instance" value
|
# Read config
|
||||||
with open('config.json', 'r') as config_file:
|
with open('config.json', 'r') as config_file:
|
||||||
config = json.load(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 = threading.Thread(target=self.update_last_played, args=(selected_instance,))
|
||||||
update_thread.start()
|
update_thread.start()
|
||||||
|
|
||||||
# Run the game using the modulecli module
|
# Build command
|
||||||
command = f"instance launch --version-override {selected_instance} {instance_value}"
|
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)
|
output = modulecli.run_command(command)
|
||||||
|
print(f"modulecli output: {output}")
|
||||||
if not output:
|
if not output:
|
||||||
raise Exception("Failed to get output from modulecli")
|
raise Exception("Failed to get output from modulecli")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_message = f"Error playing {selected_instance}: {e}"
|
error_message = f"Error playing {selected_instance}: {e}"
|
||||||
|
print(error_message) # Add this for debugging
|
||||||
logging.error(error_message)
|
logging.error(error_message)
|
||||||
# Use QMetaObject.invokeMethod to call showError safely
|
# (Show error in UI if necessary)
|
||||||
QMetaObject.invokeMethod(
|
|
||||||
self, "showError", Qt.QueuedConnection,
|
|
||||||
Q_ARG(str, "Error"), Q_ARG(str, error_message)
|
|
||||||
)
|
|
||||||
finally:
|
finally:
|
||||||
# Reset current_state to "menu" after the game closes
|
|
||||||
self.current_state = "menu"
|
self.current_state = "menu"
|
||||||
self.update_total_playtime(self.start_time)
|
self.update_total_playtime(self.start_time)
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
@ -1137,7 +1203,7 @@ class PicomcVersionSelector(QWidget):
|
|||||||
|
|
||||||
about_message = (
|
about_message = (
|
||||||
f"PicoDulce Launcher (v{version_number})\n\n"
|
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"
|
"Credits:\n"
|
||||||
"Nixietab: Code and UI design\n"
|
"Nixietab: Code and UI design\n"
|
||||||
"Wabaano: Graphic design\n"
|
"Wabaano: Graphic design\n"
|
||||||
@ -1425,7 +1491,7 @@ class ModLoaderAndVersionMenu(QDialog):
|
|||||||
|
|
||||||
if instance_name:
|
if instance_name:
|
||||||
try:
|
try:
|
||||||
# Run the "picomc instance create" command
|
# Run the "zucaro instance create" command
|
||||||
command = f"instance create {instance_name}"
|
command = f"instance create {instance_name}"
|
||||||
modulecli.run_command(command)
|
modulecli.run_command(command)
|
||||||
|
|
||||||
@ -1450,7 +1516,7 @@ class ModLoaderAndVersionMenu(QDialog):
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Run the "picomc instance rename" command
|
# Run the "zucaro instance rename" command
|
||||||
command = f"instance rename {old_instance_name} {new_instance_name}"
|
command = f"instance rename {old_instance_name} {new_instance_name}"
|
||||||
modulecli.run_command(command)
|
modulecli.run_command(command)
|
||||||
|
|
||||||
@ -1480,7 +1546,7 @@ class ModLoaderAndVersionMenu(QDialog):
|
|||||||
|
|
||||||
if confirm_delete == QMessageBox.Yes:
|
if confirm_delete == QMessageBox.Yes:
|
||||||
try:
|
try:
|
||||||
# Run the "picomc instance delete" command
|
# Run the "zucaro instance delete" command
|
||||||
command = f"instance delete {instance_name}"
|
command = f"instance delete {instance_name}"
|
||||||
modulecli.run_command(command)
|
modulecli.run_command(command)
|
||||||
|
|
||||||
@ -1496,7 +1562,7 @@ class ModLoaderAndVersionMenu(QDialog):
|
|||||||
|
|
||||||
def load_instances(self):
|
def load_instances(self):
|
||||||
try:
|
try:
|
||||||
# Run the "picomc instance list" command
|
# Run the "zucaro instance list" command
|
||||||
command = "instance list"
|
command = "instance list"
|
||||||
output = modulecli.run_command(command)
|
output = modulecli.run_command(command)
|
||||||
|
|
||||||
@ -1792,6 +1858,6 @@ if __name__ == '__main__':
|
|||||||
app.setWindowIcon(QIcon('holiday.ico')) # Set holiday icon
|
app.setWindowIcon(QIcon('holiday.ico')) # Set holiday icon
|
||||||
else:
|
else:
|
||||||
app.setWindowIcon(QIcon('launcher_icon.ico')) # Set regular icon
|
app.setWindowIcon(QIcon('launcher_icon.ico')) # Set regular icon
|
||||||
window = PicomcVersionSelector()
|
window = zucaroVersionSelector()
|
||||||
window.show()
|
window.show()
|
||||||
sys.exit(app.exec_())
|
sys.exit(app.exec_())
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
picomc
|
|
||||||
zucaro
|
zucaro
|
||||||
PyQt5
|
PyQt5
|
||||||
requests
|
requests
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "0.13.3",
|
"version": "0.13.6",
|
||||||
"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",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user