Compare commits

..

60 commits
v1.4.0 ... main

Author SHA1 Message Date
str1k3r d35927cf5b
fix: panoramas (#195)
Some checks failed
Nix / build (push) Has been cancelled
2026-08-19 01:08:15 +03:00
str1k3r 580a6bc7eb
fix(lceonline): being logged out every app open (#194) 2026-08-18 22:47:12 +03:00
github-actions[bot] ff1958614c chore: update stable Homebrew casks/formula [skip ci] 2026-08-18 13:31:13 +00:00
/home/neo b70fa735a2
silly bump (#190) 2026-08-18 15:53:02 +03:00
João Pedro Carvalho 42b3cf62e5
feat(skins): 2D integrated skin editor (direct/3D character painting … (#187)
Co-authored-by: /home/neo <158327205+neoapps-dev@users.noreply.github.com>
2026-08-18 15:39:10 +03:00
/home/neo 0e1c9250bf
1.6.1 (#189) 2026-08-18 15:36:27 +03:00
github-actions[bot] 116fda9c27 chore: update Nix stable pin to v1.6.0 [skip ci] 2026-08-18 00:12:55 +00:00
github-actions[bot] b4daee985a chore: update stable Homebrew casks/formula [skip ci] 2026-08-17 23:19:02 +00:00
/home/neo e8dd728c48
fix build issue ig (#182) 2026-08-17 21:51:50 +03:00
/home/neo d6f622c913
Merge diamond into main (#175)
Co-authored-by: str1k3r <115313679+S1l3ntStr1ke87@users.noreply.github.com>
2026-08-17 21:42:38 +03:00
João Pedro Carvalho 816997803a
fix(ClickParticles): clear remaining particles and remove click effects when animations are disabled (#178)
Some checks failed
Nix / build (push) Has been cancelled
2026-08-11 13:31:19 +03:00
João Pedro Carvalho 51a95949ad
fix(rpc): discord rpc proper shutdown when launcher closes (#179) 2026-08-11 13:30:51 +03:00
/home/neo d536a6590b
feat: xbox360+ps3+java->windows64 world conversion (#145)
Some checks failed
Nix / build (push) Has been cancelled
Co-authored-by: str1k3r <115313679+S1l3ntStr1ke87@users.noreply.github.com>
2026-08-07 19:01:24 +03:00
Edward? cb684177fe
Fix: Added stable branch to flake and needed packages (#165)
Some checks failed
Nix / build (push) Has been cancelled
2026-07-23 17:27:13 +03:00
Edward? 19380b0585
Feat: Adding homebrew cask and formula method of install (#159)
Some checks failed
Nix / build (push) Has been cancelled
2026-07-18 19:59:19 +03:00
Edward? 962979c70e
Feat: Adding Gentoo Ebuild (#148)
Some checks failed
Nix / build (push) Has been cancelled
Co-authored-by: /home/neo <158327205+neoapps-dev@users.noreply.github.com>
2026-07-17 04:28:24 +03:00
Edward? 8da6a217bc
Feat: Nix Flake install method with ci (#146)
Some checks are pending
Nix / build (push) Waiting to run
2026-07-16 21:08:19 +03:00
Jan Dróżdż 4a561a0c78
Fixed bug with the audio track skipping on Windows (#143)
Co-authored-by: /home/neo <158327205+neoapps-dev@users.noreply.github.com>
2026-07-15 15:57:45 +03:00
Byte b164a52bf8
fix(lceonline): Always use the latest download available (#139) 2026-07-14 15:25:55 +03:00
str1k3r 1c65b48958
fix(lceonline): user turning null by refreshing token when app is opened (#138) 2026-07-13 14:15:06 +03:00
/home/neo 9cf05611a3
feat(hooks/useGameManager): partnership with LapboardMC (#137) 2026-07-13 13:17:31 +03:00
str1k3r 0d316a6518
fix: appheader and panorama not showing on setup (#136) 2026-07-13 12:02:19 +03:00
/home/neo 5f04f26eb8
bump (#133) 2026-07-12 20:11:06 +03:00
/home/neo 0a32333019
1.6.0 drop 1 (#132) 2026-07-12 20:07:52 +03:00
/home/neo 0594d330be
fix(versions): custom path empty directory, fixes #127 (#131) 2026-07-12 10:06:37 +03:00
str1k3r e8038f3c3e
fix(lceonline): client not opening & webview window for lceonline login (#129) 2026-07-12 09:43:17 +03:00
/home/neo 124bbad12c
docs: readme (#126) 2026-07-11 21:24:01 +03:00
/home/neo 034fbf5737
feat: 1.5.0 drop 2 (#122)
Co-authored-by: str1k3r <115313679+S1l3ntStr1ke87@users.noreply.github.com>
2026-07-11 21:04:53 +03:00
/home/neo f46453f1cd
feat: extended description and required versions (#119) 2026-07-07 17:10:20 +03:00
/home/neo b8c448dab8
feat(workshop): dependencies support (#118) 2026-07-07 16:30:48 +03:00
/home/neo dd3048b88c
chore: bump (#117) 2026-07-06 22:11:40 +03:00
neoapps-dev 7124b4a316 chore: bump 2026-07-06 21:55:04 +03:00
/home/neo ecf3ca084f
feat(credits): more people (#115) 2026-07-04 15:56:19 +03:00
/home/neo f7177b43bd
feat(credits): more people (#114)
Co-authored-by: Sarah <asdyousif22.acc2@gmail.com>
2026-07-04 15:32:11 +03:00
/home/neo 5238a9de4e
feat(hooks/useGameManager): Moon Edition panorama (#113) 2026-07-04 14:17:51 +03:00
/home/neo d9441c9a52
feat(devtools): initial model editor (#112) 2026-07-04 14:01:20 +03:00
/home/neo 121a0bf4cf
fix(hooks/useGamepad): focus state (#110) 2026-07-02 15:52:28 +03:00
/home/neo 2e8e6cdec3
feat(devtools): community guides (#109) 2026-07-02 15:25:21 +03:00
Specifix d7eca9ea60
Fix typo in Free Multiplayer section (#107)
Corrected a typo in the README regarding multiplayer service.
2026-07-01 11:21:33 +03:00
/home/neo 58581203e0
feat(icon): macOS style (#106) 2026-06-30 22:31:11 +03:00
/home/neo 3842bee2ab
feat: bad apple (#105) 2026-06-28 23:33:12 +03:00
/home/neo 9b4d7c7167
feat(downloadoverlay): custom image (#104) 2026-06-28 22:35:12 +03:00
/home/neo 074c1e50b9
feat: titlebar dragging (#103) 2026-06-28 20:48:50 +03:00
/home/neo b5667532ca
feat: savedata parsing and 360revived new link (#101) 2026-06-28 20:36:29 +03:00
/home/neo a94e4ec758
feat: parallel downloads, redesigned download overlay (#100) 2026-06-28 18:12:40 +03:00
/home/neo 92bd9c6f2a
feat(credits): more logos and moon edition (#99) 2026-06-28 17:11:49 +03:00
/home/neo e2459777b2
feat: neoLegacy official DLCs support (#98) 2026-06-28 17:03:53 +03:00
/home/neo e2a58ab948
fix(workshop): no image for plugins, and description bug fix (#97) 2026-06-28 15:56:05 +03:00
/home/neo 62b81e5145
feat: bring back titlebar on non-macOS systems, and show on fullscreen (#96) 2026-06-28 15:26:55 +03:00
/home/neo 98f92c3173
feat: download retry and error surfacing (#95) 2026-06-28 14:38:33 +03:00
/home/neo 85855ac4af
feat: workaround neoLegacy slim skin support (#94) 2026-06-27 14:44:33 +03:00
/home/neo 2e57576200
feat(intro): skip on keypress or click (#92) 2026-06-26 21:47:01 +03:00
/home/neo 0459370846
feat: neoLegacy gitea link, placeholder for moon edition, and bluerne… (#91) 2026-06-26 21:04:44 +03:00
/home/neo ef2dbc085f
feat(GameManager): new hellish ends logo (#90) 2026-06-25 14:19:05 +03:00
str1k3r 4e5a0ebb12
fix(lceonline): no image not showing when no icon is specified (#89) 2026-06-25 13:06:29 +03:00
str1k3r a1558b6693
fix: black screen when opening server plugins (#88) 2026-06-25 01:08:02 +03:00
/home/neo 5d7d2148cc
feat(VersionsMenu): download to custom path (#87) 2026-06-24 19:58:34 +03:00
/home/neo e65ea4e06a
fix: fresh install fullscreen (#86) 2026-06-24 13:41:17 +03:00
/home/neo d90c95da7f
bump (#84) 2026-06-23 18:58:21 +03:00
neoapps-dev 587d1983b8 fix: sfx issue, closes #80 2026-06-22 22:29:03 +03:00
210 changed files with 21514 additions and 3495 deletions

View file

@ -85,7 +85,7 @@ jobs:
with:
name: ${{ matrix.artifact-name }}
path: |
src-tauri/target/*/release/bundle/**/*
src-tauri/target/**/release/bundle/**/*
build-flatpak:
runs-on: ubuntu-latest
@ -108,3 +108,139 @@ jobs:
with:
name: flatpak
path: Emerald.Legacy.Launcher.flatpak
build-appimage:
runs-on: ubuntu-latest
container:
image: archlinux:latest
steps:
- name: Bootstrap keyring and git
run: |
pacman -Syu --noconfirm archlinux-keyring
pacman -S --noconfirm --needed git
- uses: actions/checkout@v4
with:
submodules: true
- uses: actions/cache@v4
with:
path: |
src-tauri/target
node_modules
/var/cache/pacman/pkg
key: arch-${{ hashFiles('src-tauri/Cargo.lock', 'pnpm-lock.yaml') }}
restore-keys: arch-
- name: Install build dependencies
run: |
pacman -S --noconfirm --needed \
base-devel wget zsync nodejs patchelf \
cargo cargo-tauri \
gtk3 libayatana-appindicator webkit2gtk-4.1 glib-networking librsvg \
xorg-server-xvfb
- name: setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Build deb with tauri
run: |
pnpm install
cargo-tauri build --bundles deb --config '{"bundle":{"createUpdaterArtifacts":false},"build":{"beforeBuildCommand":"pnpm build"}}'
- name: Extract deb into /usr
run: |
cd src-tauri/target/release/bundle/deb
ar x ./*.deb
tar -xf data.tar.*
cp -a usr/. /usr/
- name: Build AppImage
run: |
cd pkg/AppImage
chmod +x ./AppImage.sh
./AppImage.sh
- uses: actions/upload-artifact@v4
with:
name: appimage
path: pkg/AppImage/dist/*.AppImage
build-android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: setup node
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: "pnpm"
- name: setup Java 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- name: setup Android SDK
uses: android-actions/setup-android@v4
- name: install Android SDK components
run: |
sdkmanager --install "platforms;android-36" "build-tools;36.0.0" "ndk;28.2.13676358"
yes | sdkmanager --licenses
sudo apt-get install -y glslang-tools
- name: install Rust stable with android target
uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-linux-android
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: "./src-tauri -> target"
- name: install frontend dependencies
run: pnpm install
- name: download android bundled assets
run: pnpm android:assets
- name: restore gradle wrapper jar
run: |
VER=$(sed -n 's/.*gradle-\([0-9][0-9.]*\)-bin\.zip.*/\1/p' src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties)
curl -fL -o src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar \
"https://raw.githubusercontent.com/gradle/gradle/v${VER}/gradle/wrapper/gradle-wrapper.jar"
- name: set up android signing keystore
env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
run: |
printf '%s' "$ANDROID_KEYSTORE_BASE64" | base64 -d > src-tauri/gen/android/app/release.keystore
cat > src-tauri/gen/android/app/key.properties <<EOF
keyAlias=$ANDROID_KEY_ALIAS
keyPassword=$ANDROID_KEYSTORE_PASSWORD
storeFile=release.keystore
storePassword=$ANDROID_KEYSTORE_PASSWORD
EOF
- name: build Android APK
env:
ANDROID_NDK_HOME: ${{ env.ANDROID_HOME }}/ndk/28.2.13676358
NDK_HOME: ${{ env.ANDROID_HOME }}/ndk/28.2.13676358
run: npx tauri android build --target aarch64
- name: upload APK
uses: actions/upload-artifact@v4
with:
name: android-apk
path: src-tauri/gen/android/app/build/outputs/apk/universal/release/*.apk

42
.github/workflows/nix.yml vendored Normal file
View file

@ -0,0 +1,42 @@
name: Nix
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
id-token: write
concurrency:
group: nix-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main
- name: Build stable package
run: nix build .#emerald-legacy-launcher --print-build-logs
- name: Build git package
run: nix build .#emerald-legacy-launcher-git --out-link result-git --print-build-logs
- name: Flake check
run: nix flake check --print-build-logs
- name: Verify outputs
run: |
test -x ./result/bin/emerald-legacy-launcher
test -x ./result-git/bin/emerald-legacy-launcher
nix path-info -Sh ./result ./result-git
ls -la ./result/bin/
ls -la ./result-git/bin/
ls -la ./result/share/applications/ || true

View file

@ -80,8 +80,146 @@ jobs:
uploadUpdaterJson: true
args: ${{ matrix.args }}
build-appimage:
runs-on: ubuntu-latest
container:
image: archlinux:latest
steps:
- name: Bootstrap keyring and git
run: |
pacman -Syu --noconfirm archlinux-keyring
pacman -S --noconfirm --needed git
- uses: actions/checkout@v4
with:
submodules: true
- uses: actions/cache@v4
with:
path: |
src-tauri/target
node_modules
/var/cache/pacman/pkg
key: arch-${{ hashFiles('src-tauri/Cargo.lock', 'pnpm-lock.yaml') }}
restore-keys: arch-
- name: Install build dependencies
run: |
pacman -S --noconfirm --needed \
base-devel wget zsync nodejs patchelf \
cargo cargo-tauri \
gtk3 libayatana-appindicator webkit2gtk-4.1 glib-networking librsvg \
xorg-server-xvfb
- name: setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Build deb with tauri
run: |
pnpm install
cargo-tauri build --bundles deb --config '{"bundle":{"createUpdaterArtifacts":false},"build":{"beforeBuildCommand":"pnpm build"}}'
- name: Extract deb into /usr
run: |
cd src-tauri/target/release/bundle/deb
ar x ./*.deb
tar -xf data.tar.*
cp -a usr/. /usr/
- name: Build AppImage
run: |
cd pkg/AppImage
chmod +x ./AppImage.sh
./AppImage.sh
- uses: actions/upload-artifact@v4
with:
name: appimage
path: pkg/AppImage/dist/*.AppImage* #neo: *.AppImage* because .zsync as well
build-android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: setup node
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: "pnpm"
- name: setup Java 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- name: setup Android SDK
uses: android-actions/setup-android@v4
- name: install Android SDK components
run: |
sdkmanager --install "platforms;android-36" "build-tools;36.0.0" "ndk;28.2.13676358"
yes | sdkmanager --licenses
sudo apt-get install -y glslang-tools
- name: install Rust stable with android target
uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-linux-android
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: "./src-tauri -> target"
- name: install frontend dependencies
run: pnpm install
- name: download android bundled assets
run: pnpm android:assets
- name: restore gradle wrapper jar
run: |
VER=$(sed -n 's/.*gradle-\([0-9][0-9.]*\)-bin\.zip.*/\1/p' src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties)
curl -fL -o src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar \
"https://raw.githubusercontent.com/gradle/gradle/v${VER}/gradle/wrapper/gradle-wrapper.jar"
- name: set up android signing keystore
env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
run: |
printf '%s' "$ANDROID_KEYSTORE_BASE64" | base64 -d > src-tauri/gen/android/app/release.keystore
cat > src-tauri/gen/android/app/key.properties <<EOF
keyAlias=$ANDROID_KEY_ALIAS
keyPassword=$ANDROID_KEYSTORE_PASSWORD
storeFile=release.keystore
storePassword=$ANDROID_KEYSTORE_PASSWORD
EOF
- name: build Android APK
env:
ANDROID_NDK_HOME: ${{ env.ANDROID_HOME }}/ndk/28.2.13676358
NDK_HOME: ${{ env.ANDROID_HOME }}/ndk/28.2.13676358
run: npx tauri android build --target aarch64
- name: upload APK
uses: actions/upload-artifact@v4
with:
name: android-apk
path: src-tauri/gen/android/app/build/outputs/apk/universal/release/*.apk
publish-flatpak:
needs: get-version
needs: [get-version, build-appimage, build-android]
runs-on: ubuntu-latest
permissions:
contents: write
@ -99,11 +237,22 @@ jobs:
bundle: emerald.flatpak
manifest-path: flatpak/io.github.Emerald_Legacy_Launcher.Emerald_Legacy_Launcher.yml
cache-key: flatpak-builder-${{ github.sha }}
- uses: actions/download-artifact@v4
with:
name: appimage
path: appimage
- uses: actions/download-artifact@v4
with:
name: android-apk
path: android
- uses: softprops/action-gh-release@v2
with:
body: ${{ needs.get-version.outputs.body }}
tag_name: v${{ needs.get-version.outputs.version }}
files: emerald.flatpak
files: |
emerald.flatpak
appimage/*.AppImage*
android/*.apk
draft: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -0,0 +1,47 @@
name: Update Homebrew Cask (Stable)
on:
release:
types: [published]
jobs:
update-cask:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: main
- name: Download release assets and update homebrew files
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG="${{ github.event.release.tag_name }}"
VERSION="${TAG#v}"
echo "Processing tag $TAG (version: $VERSION)"
mkdir -p dist
# Download the Intel dmg, Apple Silicon dmg, and Linux AppImage
gh release download "$TAG" --dir dist --pattern "*_x64.dmg" --pattern "*_aarch64.dmg" --pattern "*_amd64.AppImage" -R "${{ github.repository }}"
ls -l dist
# Calculate hashes
INTEL_HASH=$(sha256sum dist/*_x64.dmg | cut -d' ' -f1)
ARM_HASH=$(sha256sum dist/*_aarch64.dmg | cut -d' ' -f1)
LINUX_HASH=$(sha256sum dist/*_amd64.AppImage | cut -d' ' -f1)
echo "Intel SHA: $INTEL_HASH"
echo "ARM SHA: $ARM_HASH"
echo "Linux SHA: $LINUX_HASH"
python3 scripts/update-homebrew.py --type stable --version "$VERSION" --intel-hash "$INTEL_HASH" --arm-hash "$ARM_HASH" --linux-hash "$LINUX_HASH"
- name: Commit and push changes
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add Casks/ Formula/
git diff --quiet && git diff --staged --quiet || (git commit -m "chore: update stable Homebrew casks/formula [skip ci]" && git push origin main)

41
.github/workflows/update-nix-stable.yml vendored Normal file
View file

@ -0,0 +1,41 @@
name: Update Nix Flake (Stable)
on:
release:
types: [published]
jobs:
update-flake:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: main
- uses: DeterminateSystems/nix-installer-action@main
- name: Prefetch release source and update flake.nix
run: |
TAG="${{ github.event.release.tag_name }}"
VERSION="${TAG#v}"
echo "Processing tag $TAG (version: $VERSION)"
BASE32_HASH=$(nix-prefetch-url --unpack \
"https://github.com/LCE-Hub/LCE-Emerald-Launcher/archive/refs/tags/${TAG}.tar.gz")
SRC_HASH=$(nix hash to-sri --type sha256 "$BASE32_HASH")
echo "stableSrcHash: $SRC_HASH"
python3 scripts/update-nix-stable.py --version "$VERSION" --src-hash "$SRC_HASH"
- name: Commit and push changes
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add flake.nix
git diff --quiet && git diff --staged --quiet || (
git commit -m "chore: update Nix stable pin to ${{ github.event.release.tag_name }} [skip ci]" &&
git push origin main
)

9
.gitignore vendored
View file

@ -24,6 +24,10 @@
.DS_Store
Thumbs.db
# Nix
/result
/result-*
# Log files
*.log
@ -32,3 +36,8 @@ Thumbs.db
/*.flatpak
/emerald-repo
/ignore/*
# Android bundled runtime assets (too large for GitHub; must be present in the working tree)
/src-tauri/gen/android/app/src/main/assets/imagefs.tar.zst
/src-tauri/gen/android/app/src/main/assets/proton-*.tar.zst

3
.gitmodules vendored
View file

@ -1,3 +1,6 @@
[submodule "flatpak/shared-modules"]
path = flatpak/shared-modules
url = https://github.com/flathub/shared-modules.git
[submodule "DiamondRuntime"]
path = DiamondRuntime
url = https://github.com/LCE-Hub/DiamondRuntime.git

View file

@ -32,4 +32,17 @@ codesign --force --deep --sign - "/path/to/Emerald\ Legacy\ Launcher.app"
```sh
pnpm flatpak # or npm
```
```
## Gentoo
A Portage overlay lives in [`gentoo/`](gentoo/). See the GNU/Linux installation section in [README.md](README.md).
## Android
Building Emerald on Android is pretty easy,
```sh
pnpm android:assets #neo: fetches the rootfs and proton11
pnpm tauri android build -t aarch64
```

View file

@ -0,0 +1,18 @@
cask "lce-emerald-launcher" do
version "1.6.1"
sha256 intel: "dd86c3f63088585f71ab94acd7c3e3ec0b535fc06ef71e7770741e08fe60782f",
arm: "e2e6c50a5b0b847ed7c857ab8a4ae421fa8666096f3c9fa8bb216f21639dcfd2"
url "https://github.com/LCE-Hub/LCE-Emerald-Launcher/releases/download/v#{version}/LCE.Emerald.Launcher_#{version}_#{arch}.dmg"
name "LCE Emerald Launcher"
desc "Minecraft Legacy Console Edition Launcher"
homepage "https://github.com/LCE-Hub/LCE-Emerald-Launcher"
app "LCE Emerald Launcher/LCE Emerald Launcher.app"
zap trash: [
"~/Library/Application Support/com.emerald.legacy",
"~/Library/Preferences/com.emerald.legacy.plist",
"~/Library/Saved Application State/com.emerald.legacy.savedState",
]
end

1
DiamondRuntime Submodule

@ -0,0 +1 @@
Subproject commit c3f56e880c4c59fe591fa2bf863ea6f247886749

View file

@ -0,0 +1,19 @@
class LceEmeraldLauncher < Formula
desc "Minecraft Legacy Console Edition Launcher"
homepage "https://github.com/LCE-Hub/LCE-Emerald-Launcher-cask"
url "https://github.com/LCE-Hub/LCE-Emerald-Launcher/releases/download/v#{version}/LCE.Emerald.Launcher_#{version}_amd64.AppImage"
sha256 "3f135df96014e6bf83ced13a89ba2ec4599532104e60aea97c45d1d246de62f2"
version "1.6.1"
license "GPL-3.0-only"
depends_on :linux
def install
# Rename and install the AppImage
bin.install "LCE.Emerald.Launcher_#{version}_amd64.AppImage" => "lce-emerald-launcher"
end
test do
assert_predicate bin/"lce-emerald-launcher", :exist?
end
end

View file

@ -3,7 +3,7 @@
<h1>LCE Emerald Launcher</h1>
<p><strong>FOSS cross-platform launcher for Minecraft Legacy Console Edition</strong></p>
<p>
<img src="https://img.shields.io/badge/version-1.0.0-blue?style=flat-square" alt="Version">
<img src="https://img.shields.io/badge/version-1.6.1-blue?style=flat-square" alt="Version">
<img src="https://img.shields.io/badge/license-GPL--3.0-green?style=flat-square" alt="License">
<img src="https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20GNU/Linux-lightgrey?style=flat-square" alt="Platforms">
</p>
@ -24,6 +24,9 @@
|-------------|---------------|----------|
| <img width="1388" height="918" alt="image" src="https://github.com/user-attachments/assets/28269ade-db3b-4bff-ac15-e8a6eb53a69e" /> | <img width="1551" height="997" alt="image" src="https://github.com/user-attachments/assets/dca543ab-94cf-48ce-8e61-dfe92fe278a7" /> | <img width="1551" height="997" alt="image" src="https://github.com/user-attachments/assets/d7bc0ed2-3bf4-4704-83c3-75c716d13a65" /> |
<a href="https://github.com/LCE-Hub/LCE-Emerald-Launcher/releases/latest">
<img src="https://raw.githubusercontent.com/rubenpgrady/get-it-on-github/refs/heads/main/get-it-on-github.png" style="width: 160px">
</a>
---
@ -54,25 +57,24 @@ LCE Emerald Launcher is the easiest way to play Minecraft Legacy Console Edition
| Feature | Description |
|---------|-------------|
| **Automated Setup** | One-click installation for neoLegacy, Revelations, 360 Revived, and Hellish Ends |
| **Automated Setup** | One-click installation for neoLegacy, Revelations, 360 Revived, Hellish Ends and others |
| **Cross-Platform** | Native support for Windows, macOS (Intel & Apple Silicon), and Linux (Steam Deck is also supported!) |
| **Lightweight** | Very light RAM usage thanks to Rust backend and Tauri framework |
| **Easy Configuration** | Built-in settings for username, game parameters, and profiles |
| **Skin Viewer** | Interactive skin preview using Three.js with layer support |
| **Custom Skins** | Import and manage your own skins with local storage |
| **Custom Skins** | Import and manage your own skins and capes |
| **Controller Support** | Full gamepad navigation support (keyboard support included) |
| **Discord Rich Presence** | Show your current activity and game status on Discord |
| **Workshop** | Community content like DLCs, Textures, Skins and more |
| **Free Multiplayer** | Powered by LCELive, Emerald provides a free multiplayer service so you can play with anyone without port forwaring! |
| **Free Multiplayer** | Powered by LCEOnline, Emerald provides a free multiplayer service so you can play with anyone without port forwarding! |
| **Developer Tools** | Create and edit 4J's propriatery file formats with ease! |
| **World conversion** | Import `.ms` files, Java worlds or even an Xbox 360/PS3 world! |
| **Playtime counting** | Count your playtime and see your most active days! |
---
## Installation
<a href="https://github.com/LCE-Hub/LCE-Emerald-Launcher/releases">
<img src="https://raw.githubusercontent.com/rubenpgrady/get-it-on-github/refs/heads/main/get-it-on-github.png" style="width: 160px">
</a>
### Windows
| Format | Best For |
@ -91,6 +93,7 @@ LCE Emerald Launcher is the easiest way to play Minecraft Legacy Console Edition
|--------|-------------|
| `.dmg` (x64) | Intel Macs |
| `.dmg` (aarch64) | Apple Silicon (M-series and A-series) |
| Homebrew Cask | Intel & Apple Silicon (via tap) |
**Installation Steps:**
1. Download the appropriate DMG for your Mac
@ -99,6 +102,15 @@ LCE Emerald Launcher is the easiest way to play Minecraft Legacy Console Edition
- Right-click the app → **Open** → confirm **Open**
- Or run: `xattr -cr "/Applications/Emerald Legacy Launcher.app"`
**Homebrew:**
```bash
# Tap the repository
brew tap LCE-Hub/lce-emerald-launcher https://github.com/LCE-Hub/LCE-Emerald-Launcher
# Install stable release
brew install --cask lce-emerald-launcher
```
### GNU/Linux
Multiple distribution formats available:
@ -109,6 +121,8 @@ Multiple distribution formats available:
| `.rpm` | RHEL, Fedora, openSUSE |
| `.AppImage` | Universal (no installation required) |
| `.flatpak` | Universal with sandboxing (recommended over AppImage) |
| Nix flake | NixOS and any Linux with [Nix](https://nixos.org/download/) installed |
| Homebrew | Any Linux with [Homebrew](https://brew.sh/) installed |
**AUR:**
Special thanks to [AntiApple4life](https://aur.archlinux.org/packages?O=0&SeB=m&K=AntiApple4life) for the AUR packages!
@ -123,11 +137,50 @@ paru -S emerald-legacy-launcher # or yay
paru -S emerald-legacy-launcher-bin # or yay
```
**Nix:**
Requires [Nix](https://nixos.org/download/) with flakes enabled (`experimental-features = nix-command flakes` in `nix.conf`).
```bash
# stable release (default) — tracks https://github.com/LCE-Hub/LCE-Emerald-Launcher/releases
nix run github:LCE-Hub/LCE-Emerald-Launcher
# or explicitly:
nix run github:LCE-Hub/LCE-Emerald-Launcher#emerald-legacy-launcher
# git / latest main
nix run github:LCE-Hub/LCE-Emerald-Launcher#emerald-legacy-launcher-git
# Install to your user profile
nix profile install github:LCE-Hub/LCE-Emerald-Launcher # stable
nix profile install github:LCE-Hub/LCE-Emerald-Launcher#emerald-legacy-launcher-git # git
```
On NixOS, add the flake as an input and install `packages.<system>.default` / `emerald-legacy-launcher` (stable) or `emerald-legacy-launcher-git` (main).
**Gentoo:**
This repository includes a local Portage overlay under [`gentoo/`](gentoo/). Point `repos.conf` at that directory, then emerge the package (build fetches Cargo/npm deps over the network; Wine/Proton is needed at runtime to launch games):
```bash
# /etc/portage/repos.conf/emerald-legacy-launcher.conf
[emerald-legacy-launcher]
location = /path/to/LCE-Emerald-Launcher/gentoo
auto-sync = no
sudo emerge games-util/emerald-legacy-launcher # 1.6.1
# sudo emerge =games-util/emerald-legacy-launcher-9999 # live git
```
**Flatpak Installation:**
```bash
flatpak install emerald.flatpak
```
**Homebrew:**
```bash
# Tap the repository
brew tap LCE-Hub/lce-emerald-launcher https://github.com/LCE-Hub/LCE-Emerald-Launcher
# Install stable release
brew install lce-emerald-launcher
```
**Dependencies (if building from source):**
```bash
# Ubuntu/Debian
@ -184,7 +237,8 @@ sudo apt install --reinstall libwebkit2gtk-4.1-0
- **The Emerald Team** - Technical development and maintenance
- **4J Studios & Mojang** - Original creators of Legacy Console Edition
- **The LCE Community** - Research and foundations for LCE on PC
- **Veroxsity (Racoon)** - Original creator of LCELive
- **Str1k3r** - Original creator of LCEOnline
- **DTention** - Original creator of [LCE-Save-Converter](https://github.com/dtentiion/LCE-Save-Converter)
---

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

After

Width:  |  Height:  |  Size: 2.1 MiB

View file

@ -1,31 +1,25 @@
![Release Artwork](https://raw.githubusercontent.com/LCE-Hub/LCE-Emerald-Launcher/refs/heads/main/RELEASE_ARTWORK.png)
- **Launcher Plugins**!
Emerald can now be extended or modified using Plugins, new menus, features, whatever!
- Fix **Hellish Ends**'s download link
- Fix **neoLegacy**'s download link
- New and improved UI!
- Customizability features for instances (Panorama and Title Image)
- A lot more splash texts by the community!
- New splash screen intro!
- Fullscreen support
- Skip intro option in settings
- Sound effects for notification toasts
- New credits screen!
- Skin viewer now in the skin changing menu
- Fix performance issues on the screenshots menu
- **Deeplinks**! You can now call Emerald actions using the `emerald://` protocol
- Improved Discord RPC
- A little bit more easter eggs
- Add `unzip` as fallback to `bsdtar` on GNU/Linux
- End partnership with Relic Network.
- No more portable binaries. Installer has custom paths option.
- Bytebukkit has been renamed to LCEOnline
- Gitea and Forgejo support for instances
- Delete files *after* downloading rather than *before* that
## Screenshots
| | | | |
|-|-|-|-|
| <img width="1328" height="853" alt="image" src="https://github.com/user-attachments/assets/14485a65-d514-4f05-8b11-c60cd4130798" /> | <img width="1328" height="853" alt="image" src="https://github.com/user-attachments/assets/5bddf800-df54-4564-96ad-5428ba68abc5" /> | <img width="1328" height="853" alt="image" src="https://github.com/user-attachments/assets/77cd6977-506c-4dbb-9bf6-8447aeb2d416" /> | <img width="1328" height="853" alt="image" src="https://github.com/user-attachments/assets/9d6eaa12-2bea-416e-9650-f20b5864ea72" /> |
- Fix title bar and panorama not showing on setup
- **Partnership with LapboardMC!**
- **Removed LCEOnline Client, LCEOnline functionality is built into Emerald**
- Fix audio skipping bug on Windows
- Gentoo, Nix flakes and Homebrew support
- **Xbox 360, PS3 and Java -> LCE world importing support (beta!)**
- Proper Discord RPC shutdown
- Remove click effects when disabling animations
- **FULL Android Port powered by our own DiamondRuntime for running the game!**
- Fix neoLegacy downloading issue
- **A lot of UI polish (thanks piebot!)**
- Add 2 default capes
- Add more default skins
- **Emerald Arguments.Schema support!**
- **Partnership with Amythest LCE**
- Fix Slim skins rendering in-launcher
- Randomize splash texts
- **Partnership with Project Lost Legacy (soon!)**
- Add Player Head to Discord RPC
- Make skin face inwards instead of outwards
- Game crash log viewer support!
- **Intel macOS support**
- And more that I forgot!

27
flake.lock Normal file
View file

@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1784120854,
"narHash": "sha256-KesHgItiZPgGX740axSiQLcIQ8D24MDqNpkKYWIek8k=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "753cc8a3a87467296ddd1fa93f0cc3e81120ee46",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

107
flake.nix Normal file
View file

@ -0,0 +1,107 @@
{
description = "LCE Emerald Launcher — FOSS cross-platform launcher for Minecraft Legacy Console Edition";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs =
{ self, nixpkgs }:
let
inherit (nixpkgs) lib;
systems = [
"x86_64-linux"
"aarch64-linux"
"x86_64-darwin"
"aarch64-darwin"
];
forAllSystems = lib.genAttrs systems;
pkgsFor =
system:
import nixpkgs {
inherit system;
overlays = [ self.overlays.default ];
};
# Bump these together when packaging a new upstream stable release.
stableVersion = "1.6.0";
stableSrcHash = "sha256-VlqBCZa42MLmJ1nUNlH7PIcLp/X7mBLCS/hm/ISiHNA=";
in
{
overlays.default = final: _prev: {
emerald-legacy-launcher = final.callPackage ./nix/package.nix {
version = stableVersion;
src = final.fetchFromGitHub {
owner = "LCE-Hub";
repo = "LCE-Emerald-Launcher";
rev = "v${stableVersion}";
hash = stableSrcHash;
};
};
emerald-legacy-launcher-git = final.callPackage ./nix/package.nix {
version = "unstable-${self.shortRev or self.dirtyShortRev or "dirty"}";
src = self;
};
};
packages = forAllSystems (
system:
let
pkgs = pkgsFor system;
in
{
default = pkgs.emerald-legacy-launcher;
emerald-legacy-launcher = pkgs.emerald-legacy-launcher;
emerald-legacy-launcher-git = pkgs.emerald-legacy-launcher-git;
}
);
apps = forAllSystems (system: {
default = {
type = "app";
program = lib.getExe self.packages.${system}.default;
};
emerald-legacy-launcher-git = {
type = "app";
program = lib.getExe self.packages.${system}.emerald-legacy-launcher-git;
};
});
checks = forAllSystems (system: {
package = self.packages.${system}.default;
package-git = self.packages.${system}.emerald-legacy-launcher-git;
});
devShells = forAllSystems (
system:
let
pkgs = pkgsFor system;
in
{
default = pkgs.mkShell {
inputsFrom = [ pkgs.emerald-legacy-launcher-git ];
packages = with pkgs; [
cargo
rustc
rustfmt
clippy
cargo-tauri
nodejs
pnpm_10
unzip
libarchive
python3
pkg-config
];
shellHook = ''
echo "Emerald Legacy Launcher dev shell"
echo " pnpm install && pnpm tauri dev"
'';
};
}
);
formatter = forAllSystems (system: (pkgsFor system).nixfmt);
};
}

View file

@ -12,7 +12,7 @@
<url type="homepage">https://github.com</url>
<developer_name>Emerald Team</developer_name>
<releases>
<release version="1.4.0" date="2026-03-19">
<release version="1.6.0" date="2026-03-19">
<description>
<p>Latest stable release of the Emerald Legacy Launcher.</p>
</description>

View file

@ -0,0 +1 @@
DIST emerald-legacy-launcher-1.5.1.tar.gz 18500442 BLAKE2B 365d9dd36ee6fddb90625719b599c8fc492eba4ce0fb8926c953f2a7d7f4987bf77d91de6a140a489534ba61e8a71bf9e65d8e015a35af31bccb84ca3a023178 SHA512 c713ef81e56d55d7135fa4228f875822a3eee38ace86d8d28a59a8b4afeff28844bf698e9902eb66075b425a0fb71ae72969ae1fe99ce185a005b5f42a4dccc8

View file

@ -0,0 +1,79 @@
# Copyright 2026 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
EAPI=8
inherit desktop xdg
DESCRIPTION="FOSS cross-platform launcher for Minecraft Legacy Console Edition"
HOMEPAGE="https://github.com/LCE-Hub/LCE-Emerald-Launcher"
SRC_URI="https://github.com/LCE-Hub/LCE-Emerald-Launcher/archive/refs/tags/v${PV}.tar.gz -> ${P}.tar.gz"
S="${WORKDIR}/LCE-Emerald-Launcher-${PV}"
LICENSE="GPL-3"
SLOT="0"
KEYWORDS="~amd64"
# Cargo crates and npm packages are fetched at build time.
RESTRICT="network-sandbox"
RDEPEND="
dev-libs/glib:2
dev-libs/libayatana-appindicator
dev-libs/openssl:=
net-libs/libsoup:3.0
net-libs/webkit-gtk:4.1
x11-libs/cairo
x11-libs/gdk-pixbuf:2
x11-libs/gtk+:3
x11-libs/pango
"
DEPEND="${RDEPEND}"
BDEPEND="
|| (
>=dev-lang/rust-1.77.0:*
>=dev-lang/rust-bin-1.77.0:*
)
net-libs/nodejs[npm]
virtual/pkgconfig
"
# Rust binaries ignore *FLAGS from make.conf.
QA_FLAGS_IGNORED="usr/bin/emerald-legacy-launcher"
src_prepare() {
default
# Disable updater artifact signing (no Tauri signing key in distro builds).
sed -i \
-e 's/"createUpdaterArtifacts": true/"createUpdaterArtifacts": false/' \
src-tauri/tauri.conf.json || die
}
src_compile() {
local -x CI=true
local -x npm_config_audit=false
local -x npm_config_fund=false
local -x npm_config_update_notifier=false
npm install || die "npm install failed"
npm run tauri -- build --no-bundle || die "tauri build failed"
}
src_install() {
dobin src-tauri/target/release/emerald-legacy-launcher
newicon -s 32 src-tauri/icons/32x32.png emerald-legacy-launcher.png
newicon -s 64 src-tauri/icons/64x64.png emerald-legacy-launcher.png
newicon -s 128 src-tauri/icons/128x128.png emerald-legacy-launcher.png
newicon -s 256 src-tauri/icons/128x128@2x.png emerald-legacy-launcher.png
newicon -s 512 src-tauri/icons/icon.png emerald-legacy-launcher.png
domenu "${FILESDIR}"/emerald-legacy-launcher.desktop
insinto /usr/share/metainfo
newins flatpak/io.github.Emerald_Legacy_Launcher.Emerald_Legacy_Launcher.metainfo.xml \
io.github.Emerald_Legacy_Launcher.Emerald_Legacy_Launcher.metainfo.xml
dodoc README.md LICENSE
}

View file

@ -0,0 +1,79 @@
# Copyright 2026 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
EAPI=8
inherit desktop git-r3 xdg
DESCRIPTION="FOSS cross-platform launcher for Minecraft Legacy Console Edition"
HOMEPAGE="https://github.com/LCE-Hub/LCE-Emerald-Launcher"
EGIT_REPO_URI="https://github.com/LCE-Hub/LCE-Emerald-Launcher.git"
LICENSE="GPL-3"
SLOT="0"
PROPERTIES="live"
# Cargo crates and npm packages are fetched at build time.
RESTRICT="network-sandbox"
RDEPEND="
dev-libs/glib:2
dev-libs/libayatana-appindicator
dev-libs/openssl:=
net-libs/libsoup:3.0
net-libs/webkit-gtk:4.1
x11-libs/cairo
x11-libs/gdk-pixbuf:2
x11-libs/gtk+:3
x11-libs/pango
"
DEPEND="${RDEPEND}"
BDEPEND="
|| (
>=dev-lang/rust-1.77.0:*
>=dev-lang/rust-bin-1.77.0:*
)
net-libs/nodejs[npm]
virtual/pkgconfig
"
# Rust binaries ignore *FLAGS from make.conf.
QA_FLAGS_IGNORED="usr/bin/emerald-legacy-launcher"
src_prepare() {
default
# Prefer npm for the frontend build; disable updater artifact signing
# (no Tauri signing key in distro builds).
sed -i \
-e 's/"createUpdaterArtifacts": true/"createUpdaterArtifacts": false/' \
src-tauri/tauri.conf.json || die
}
src_compile() {
local -x CI=true
local -x npm_config_audit=false
local -x npm_config_fund=false
local -x npm_config_update_notifier=false
npm install || die "npm install failed"
npm run tauri -- build --no-bundle || die "tauri build failed"
}
src_install() {
dobin src-tauri/target/release/emerald-legacy-launcher
newicon -s 32 src-tauri/icons/32x32.png emerald-legacy-launcher.png
newicon -s 64 src-tauri/icons/64x64.png emerald-legacy-launcher.png
newicon -s 128 src-tauri/icons/128x128.png emerald-legacy-launcher.png
newicon -s 256 src-tauri/icons/128x128@2x.png emerald-legacy-launcher.png
newicon -s 512 src-tauri/icons/icon.png emerald-legacy-launcher.png
domenu "${FILESDIR}"/emerald-legacy-launcher.desktop
insinto /usr/share/metainfo
newins flatpak/io.github.Emerald_Legacy_Launcher.Emerald_Legacy_Launcher.metainfo.xml \
io.github.Emerald_Legacy_Launcher.Emerald_Legacy_Launcher.metainfo.xml
dodoc README.md LICENSE
}

View file

@ -0,0 +1,11 @@
[Desktop Entry]
Name=LCE Emerald Launcher
Comment=FOSS cross-platform launcher for Minecraft Legacy Console Edition
Exec=emerald-legacy-launcher %u
Icon=emerald-legacy-launcher
Type=Application
Categories=Game;
MimeType=x-scheme-handler/emerald;x-scheme-handler/emeraldlauncher;x-scheme-handler/discord-1482504445152460871;
StartupNotify=true
StartupWMClass=emerald-legacy-launcher
Terminal=false

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE pkgmetadata SYSTEM "https://www.gentoo.org/dtd/metadata.dtd">
<pkgmetadata>
<upstream>
<remote-id type="github">LCE-Hub/LCE-Emerald-Launcher</remote-id>
<bugs-to>https://github.com/LCE-Hub/LCE-Emerald-Launcher/issues</bugs-to>
</upstream>
<longdescription lang="en">
LCE Emerald Launcher is a FOSS cross-platform Tauri launcher for
Minecraft Legacy Console Edition. It installs community builds,
manages versions, skins, and workshop content, and launches the game
via Wine/Proton on Linux.
</longdescription>
</pkgmetadata>

View file

@ -0,0 +1,3 @@
masters = gentoo
thin-manifests = true
sign-manifests = false

View file

@ -0,0 +1 @@
emerald-legacy-launcher

View file

@ -4,6 +4,14 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script>
if (/Android/i.test(navigator.userAgent)) {
document.documentElement.classList.add("is-android");
document
.querySelector('meta[name="viewport"]')
.setAttribute("content", "width=1280");
}
</script>
<link rel="preload" href="/fonts/Mojangles.ttf" as="font" type="font/truetype" crossorigin>
<link rel="preload" href="/images/MenuTitle.png" as="image">
<title>Emerald Legacy Launcher</title>

135
nix/package.nix Normal file
View file

@ -0,0 +1,135 @@
{
lib,
stdenv,
rustPlatform,
cargo-tauri,
nodejs,
pkg-config,
pnpm_10,
fetchPnpmDeps,
pnpmConfigHook,
wrapGAppsHook4,
openssl,
webkitgtk_4_1,
glib-networking,
libayatana-appindicator,
librsvg,
udev,
version ? "1.5.1",
src ? null,
}:
# Disable updater artifact signing — Nix builds have no Tauri signing key.
let
tauriConfOverride = builtins.toFile "tauri-nix.conf.json" ''
{
"bundle": {
"createUpdaterArtifacts": false
}
}
'';
defaultSrc = lib.cleanSourceWith {
src = ../.;
filter =
path: _type:
let
baseName = baseNameOf path;
in
!(builtins.elem baseName [
".git"
"node_modules"
"target"
"dist"
"build-flatpak"
"emerald-repo"
"result"
"result-dev"
]);
};
in
rustPlatform.buildRustPackage (finalAttrs: {
pname = "emerald-legacy-launcher";
inherit version;
src = if src != null then src else defaultSrc;
cargoRoot = "src-tauri";
buildAndTestSubdir = finalAttrs.cargoRoot;
cargoHash = "sha256-tSbFyle2eQ5Dw9fTXloIepOZU1UxhkF6/KZBRXehm4w=";
pnpmDeps = fetchPnpmDeps {
inherit (finalAttrs) pname version src;
pnpm = pnpm_10;
fetcherVersion = 3;
hash = "sha256-6na8YlSTkdnSse8DQjBnTBpfmKSYj3+1FLWVyyfUx1g=";
};
nativeBuildInputs = [
cargo-tauri.hook
nodejs
pkg-config
pnpmConfigHook
pnpm_10
]
++ lib.optionals stdenv.hostPlatform.isLinux [ wrapGAppsHook4 ];
buildInputs = [
openssl
]
++ lib.optionals stdenv.hostPlatform.isLinux [
glib-networking
libayatana-appindicator
librsvg
udev
webkitgtk_4_1
];
tauriBuildFlags = [
"-c"
tauriConfOverride
];
postPatch = lib.optionalString stdenv.hostPlatform.isLinux ''
substituteInPlace "$cargoDepsCopy"/*/libappindicator-sys-*/src/lib.rs \
--replace-fail "libayatana-appindicator3.so.1" \
"${libayatana-appindicator}/lib/libayatana-appindicator3.so.1"
'';
# Prefer the project's pnpm frontend build over the npm beforeBuildCommand.
preBuild = ''
substituteInPlace src-tauri/tauri.conf.json \
--replace-fail '"beforeBuildCommand": "npm run build"' \
'"beforeBuildCommand": "pnpm run build"'
'';
preFixup = lib.optionalString stdenv.hostPlatform.isLinux ''
gappsWrapperArgs+=(
--prefix LD_LIBRARY_PATH : ${
lib.makeLibraryPath [
libayatana-appindicator
udev
]
}
)
'';
doCheck = false;
env = {
CI = "true";
};
meta = {
description = "FOSS cross-platform launcher for Minecraft Legacy Console Edition";
homepage = "https://github.com/LCE-Hub/LCE-Emerald-Launcher";
changelog = lib.mkIf (!(lib.hasPrefix "unstable-" version)) (
"https://github.com/LCE-Hub/LCE-Emerald-Launcher/releases/tag/v${version}"
);
license = lib.licenses.gpl3Only;
mainProgram = "emerald-legacy-launcher";
platforms = lib.platforms.linux ++ lib.platforms.darwin;
maintainers = [ ];
};
})

View file

@ -1,7 +1,7 @@
{
"name": "emerald-legacy-launcher",
"private": true,
"version": "1.4.0",
"version": "1.6.1",
"type": "module",
"scripts": {
"dev": "vite",
@ -10,6 +10,7 @@
"tauri": "tauri",
"tauri:build": "tauri build && pnpm run post-build:macos",
"post-build:macos": "bash src-tauri/scripts/post-build-macos.sh",
"android:assets": "node scripts/download-android-assets.mjs",
"flatpak": "flatpak-builder --user --install-deps-from=flathub --repo=emerald-repo --force-clean build-flatpak flatpak/io.github.Emerald_Legacy_Launcher.Emerald_Legacy_Launcher.yml && flatpak build-bundle emerald-repo emerald.flatpak io.github.Emerald_Legacy_Launcher.Emerald_Legacy_Launcher"
},
"dependencies": {
@ -24,6 +25,8 @@
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-markdown": "^10.1.0",
"rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0",
"remark-gfm": "^4.0.1",
"three": "^0.183.2"
},

48
pkg/AppImage/AppImage.sh Executable file
View file

@ -0,0 +1,48 @@
#!/bin/sh
#neo: credits: https://github.com/cjonas1999/OverBind/blob/master/pkg/AppImage/AppImage.sh
set -eu
ARCH="$(uname -m)"
SHARUN_REPO="https://raw.githubusercontent.com/pkgforge-dev/Anylinux-AppImages"
_commits_json=$(mktemp)
wget -qO "$_commits_json" \
"https://api.github.com/repos/pkgforge-dev/Anylinux-AppImages/commits?path=useful-tools/&per_page=100"
STABLE_SHA=$(node -e "
const data = JSON.parse(require('fs').readFileSync('$_commits_json', 'utf8'));
const now = Date.now(), h24 = 864e5;
for (let i = 0; i < data.length; i++) {
const t = new Date(data[i].commit.committer.date).getTime();
if (now - t < h24) continue;
const prev = data[i - 1];
if (!prev || new Date(prev.commit.committer.date).getTime() - t >= h24) {
process.stdout.write(data[i].sha);
process.exit(0);
}
}
process.stderr.write('No settled commit found in last 100 commits\n');
process.exit(1);
")
rm -f "$_commits_json"
echo "Pinning sharun scripts to settled commit $STABLE_SHA"
SHARUN="$SHARUN_REPO/$STABLE_SHA/useful-tools/quick-sharun.sh"
DEBLOATED_PKGS="$SHARUN_REPO/$STABLE_SHA/useful-tools/get-debloated-pkgs.sh"
#export UPINFO="gh-releases-zsync|${GITHUB_REPOSITORY%/*}|${GITHUB_REPOSITORY#*/}|latest|*$ARCH.AppImage.zsync"
export OUTNAME=Emerald-Legacy-Launcher-anylinux-"$ARCH".AppImage
export DESKTOP="/usr/share/applications/LCE Emerald Launcher.desktop"
export ICON=/usr/share/icons/hicolor/256x256@2/apps/emerald-legacy-launcher.png
export DEPLOY_OPENGL=1
rm -rf AppDir dist appinfo
wget --retry-connrefused --tries=30 "$DEBLOATED_PKGS" -O ./get-debloated-pkgs
wget --retry-connrefused --tries=30 "$SHARUN" -O ./quick-sharun
chmod +x ./quick-sharun ./get-debloated-pkgs
./get-debloated-pkgs --add-common --prefer-nano
./quick-sharun \
/usr/bin/emerald-legacy-launcher \
/usr/lib/libayatana-appindicator*.so*
mkdir -p "AppDir/usr/lib"
cp -r "/usr/lib/LCE Emerald Launcher" "AppDir/usr/lib/"
./quick-sharun --make-appimage
mkdir -p ./dist
mv -v ./*.AppImage* ./dist
echo "All Done!"

View file

@ -41,6 +41,12 @@ importers:
react-markdown:
specifier: ^10.1.0
version: 10.1.0(@types/react@19.2.14)(react@19.2.4)
rehype-raw:
specifier: ^7.0.0
version: 7.0.0
rehype-sanitize:
specifier: ^6.0.0
version: 6.0.0
remark-gfm:
specifier: ^4.0.1
version: 4.0.1
@ -860,6 +866,10 @@ packages:
resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==}
engines: {node: '>=10.13.0'}
entities@6.0.1:
resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
engines: {node: '>=0.12'}
esbuild@0.27.4:
resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==}
engines: {node: '>=18'}
@ -928,15 +938,36 @@ packages:
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
hast-util-from-parse5@8.0.3:
resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==}
hast-util-parse-selector@4.0.0:
resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==}
hast-util-raw@9.1.0:
resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==}
hast-util-sanitize@5.0.2:
resolution: {integrity: sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==}
hast-util-to-jsx-runtime@2.3.6:
resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
hast-util-to-parse5@8.0.1:
resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==}
hast-util-whitespace@3.0.0:
resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
hastscript@9.0.1:
resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==}
html-url-attributes@3.0.1:
resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
html-void-elements@3.0.0:
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
inline-style-parser@0.2.7:
resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==}
@ -1230,6 +1261,9 @@ packages:
parse-entities@4.0.2:
resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==}
parse5@7.3.0:
resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@ -1279,6 +1313,12 @@ packages:
resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==}
engines: {node: '>=0.10.0'}
rehype-raw@7.0.0:
resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==}
rehype-sanitize@6.0.0:
resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==}
remark-gfm@4.0.1:
resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
@ -1400,6 +1440,9 @@ packages:
peerDependencies:
browserslist: '>= 4.21.0'
vfile-location@5.0.3:
resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==}
vfile-message@4.0.3:
resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==}
@ -1446,6 +1489,9 @@ packages:
yaml:
optional: true
web-namespaces@2.0.1:
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
which-module@2.0.1:
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
@ -2090,6 +2136,8 @@ snapshots:
graceful-fs: 4.2.11
tapable: 2.3.0
entities@6.0.1: {}
esbuild@0.27.4:
optionalDependencies:
'@esbuild/aix-ppc64': 0.27.4
@ -2158,6 +2206,43 @@ snapshots:
graceful-fs@4.2.11: {}
hast-util-from-parse5@8.0.3:
dependencies:
'@types/hast': 3.0.4
'@types/unist': 3.0.3
devlop: 1.1.0
hastscript: 9.0.1
property-information: 7.1.0
vfile: 6.0.3
vfile-location: 5.0.3
web-namespaces: 2.0.1
hast-util-parse-selector@4.0.0:
dependencies:
'@types/hast': 3.0.4
hast-util-raw@9.1.0:
dependencies:
'@types/hast': 3.0.4
'@types/unist': 3.0.3
'@ungap/structured-clone': 1.3.0
hast-util-from-parse5: 8.0.3
hast-util-to-parse5: 8.0.1
html-void-elements: 3.0.0
mdast-util-to-hast: 13.2.1
parse5: 7.3.0
unist-util-position: 5.0.0
unist-util-visit: 5.1.0
vfile: 6.0.3
web-namespaces: 2.0.1
zwitch: 2.0.4
hast-util-sanitize@5.0.2:
dependencies:
'@types/hast': 3.0.4
'@ungap/structured-clone': 1.3.0
unist-util-position: 5.0.0
hast-util-to-jsx-runtime@2.3.6:
dependencies:
'@types/estree': 1.0.8
@ -2178,12 +2263,32 @@ snapshots:
transitivePeerDependencies:
- supports-color
hast-util-to-parse5@8.0.1:
dependencies:
'@types/hast': 3.0.4
comma-separated-tokens: 2.0.3
devlop: 1.1.0
property-information: 7.1.0
space-separated-tokens: 2.0.2
web-namespaces: 2.0.1
zwitch: 2.0.4
hast-util-whitespace@3.0.0:
dependencies:
'@types/hast': 3.0.4
hastscript@9.0.1:
dependencies:
'@types/hast': 3.0.4
comma-separated-tokens: 2.0.3
hast-util-parse-selector: 4.0.0
property-information: 7.1.0
space-separated-tokens: 2.0.2
html-url-attributes@3.0.1: {}
html-void-elements@3.0.0: {}
inline-style-parser@0.2.7: {}
is-alphabetical@2.0.1: {}
@ -2654,6 +2759,10 @@ snapshots:
is-decimal: 2.0.1
is-hexadecimal: 2.0.1
parse5@7.3.0:
dependencies:
entities: 6.0.1
path-exists@4.0.0: {}
picocolors@1.1.1: {}
@ -2705,6 +2814,17 @@ snapshots:
react@19.2.4: {}
rehype-raw@7.0.0:
dependencies:
'@types/hast': 3.0.4
hast-util-raw: 9.1.0
vfile: 6.0.3
rehype-sanitize@6.0.0:
dependencies:
'@types/hast': 3.0.4
hast-util-sanitize: 5.0.2
remark-gfm@4.0.1:
dependencies:
'@types/mdast': 4.0.4
@ -2876,6 +2996,11 @@ snapshots:
escalade: 3.2.0
picocolors: 1.1.1
vfile-location@5.0.3:
dependencies:
'@types/unist': 3.0.3
vfile: 6.0.3
vfile-message@4.0.3:
dependencies:
'@types/unist': 3.0.3
@ -2900,6 +3025,8 @@ snapshots:
jiti: 2.6.1
lightningcss: 1.31.1
web-namespaces@2.0.1: {}
which-module@2.0.1: {}
wrap-ansi@6.2.0:

BIN
public/Capes/4J.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

BIN
public/Skins/ava.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
public/Skins/byte.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
public/Skins/counterr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
public/Skins/kowhaifan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
public/Skins/str1k3r.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 B

BIN
public/Skins/tranq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 937 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 59 KiB

260
schemas/args.json Normal file
View file

@ -0,0 +1,260 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://emerald-legacy-launcher/schemas/args.json",
"title": "ArgsSchema",
"description": "Schema describing launcher command-line argument options, groups, and conditional dependencies.",
"type": "object",
"additionalProperties": false,
"required": ["$schema", "groups", "options", "dependencies"],
"properties": {
"$schema": {
"type": "string",
"description": "URI of the JSON schema this file conforms to."
},
"schemaVersion": {
"type": "integer",
"const": 1,
"description": "Version of the schema format. Currently always 1."
},
"meta": {
"type": "object",
"additionalProperties": true,
"description": "Arbitrary metadata about this schema."
},
"groups": {
"type": "array",
"description": "Groups used to organize options.",
"items": {
"$ref": "#/$defs/schemaGroup"
}
},
"options": {
"type": "array",
"description": "The command-line options.",
"items": {
"$ref": "#/$defs/schemaOption"
}
},
"dependencies": {
"type": "array",
"description": "Conditional dependencies that disable or hide options.",
"items": {
"$ref": "#/$defs/schemaDependency"
}
}
},
"$defs": {
"schemaGroup": {
"type": "object",
"additionalProperties": false,
"required": ["id", "title"],
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the group."
},
"title": {
"type": "string",
"description": "Display title of the group."
},
"description": {
"type": "string",
"description": "Optional description of the group."
}
}
},
"schemaOption": {
"type": "object",
"additionalProperties": false,
"required": ["id", "title", "type", "arg"],
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the option."
},
"title": {
"type": "string",
"description": "Display title of the option."
},
"type": {
"enum": ["boolean", "int", "number", "string", "choice"],
"description": "The kind of value the option accepts."
},
"arg": {
"type": "string",
"description": "The command-line argument string emitted for this option."
},
"description": {
"type": "string",
"description": "Optional human-readable description."
},
"group": {
"type": "string",
"description": "Identifier of the group this option belongs to."
},
"default": {
"description": "Default value for the option. Must match the option's type."
},
"min": {
"type": "number",
"description": "Minimum value for int/number options."
},
"max": {
"type": "number",
"description": "Maximum value for int/number options."
},
"step": {
"type": "number",
"description": "Stepping increment for int/number options."
},
"placeholder": {
"type": "string",
"description": "Placeholder text for string options."
},
"choices": {
"type": "array",
"description": "Allowed choices. Required and must be non-empty when type is 'choice'.",
"minItems": 1,
"items": {
"$ref": "#/$defs/schemaChoice"
}
}
},
"allOf": [
{
"if": {
"properties": {
"type": { "const": "choice" }
},
"required": ["type"]
},
"then": {
"required": ["choices"]
}
},
{
"if": {
"properties": {
"type": { "const": "boolean" }
},
"required": ["type"]
},
"then": {
"properties": {
"default": { "type": "boolean" }
}
}
},
{
"if": {
"properties": {
"type": { "const": "string" }
},
"required": ["type"]
},
"then": {
"properties": {
"default": { "type": "string" }
}
}
}
]
},
"schemaChoice": {
"type": "object",
"additionalProperties": false,
"required": ["value"],
"properties": {
"value": {
"type": "string",
"description": "The value stored/emitted for this choice."
},
"label": {
"type": "string",
"description": "Optional display label. Defaults to the value."
}
}
},
"schemaDependency": {
"type": "object",
"additionalProperties": false,
"required": ["target", "when"],
"properties": {
"target": {
"type": "string",
"description": "Identifier of the option affected by this dependency."
},
"when": {
"$ref": "#/$defs/condition",
"description": "Condition that, when true, applies the effect."
},
"effect": {
"enum": ["disable", "hide"],
"description": "Whether the target option is disabled or hidden. Defaults to 'disable'."
}
}
},
"condition": {
"oneOf": [
{
"type": "object",
"additionalProperties": false,
"required": ["option"],
"properties": {
"option": {
"type": "string",
"description": "Identifier of the option to inspect."
},
"equals": {
"description": "True when the option value strictly equals this."
},
"in": {
"type": "array",
"description": "True when the option value is one of these.",
"items": true
},
"not": {
"description": "True when the option value is not strictly equal to this."
},
"exists": {
"type": "boolean",
"description": "True when the option value is present (or absent when false)."
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": ["all"],
"properties": {
"all": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#/$defs/condition" }
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": ["any"],
"properties": {
"any": {
"type": "array",
"minItems": 1,
"items": { "$ref": "#/$defs/condition" }
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": ["not"],
"properties": {
"not": { "$ref": "#/$defs/condition" }
}
}
]
}
}
}

View file

@ -0,0 +1,109 @@
#!/usr/bin/env node
import {
createWriteStream,
createReadStream,
existsSync,
unlinkSync,
} from "node:fs";
import { mkdir, rename } from "node:fs/promises";
import { createHash } from "node:crypto";
import { Readable } from "node:stream";
import { fileURLToPath } from "node:url";
import path from "node:path";
const RELEASE_TAG = "android-assets-v1";
const REPO = "LCE-Hub/LCE-Emerald-Launcher";
const ASSET_DIR = fileURLToPath(
new URL("../src-tauri/gen/android/app/src/main/assets/", import.meta.url),
);
const ASSETS = [
{
file: "imagefs.tar.zst",
sha256: "7838756e6a05c91afff68f4bf12aa2780f815877753f8dd354e203e99b9caf8a",
},
{
file: "proton-11.0-arm64ec.tar.zst",
sha256: "ceb768396c2d44256518f94ae236f0c1f40738cf050a52e990e2fef67ff161df",
},
];
function sha256File(file) {
return new Promise((resolve, reject) => {
const hash = createHash("sha256");
const stream = createReadStream(file);
stream.on("data", (chunk) => hash.update(chunk));
stream.on("end", () => resolve(hash.digest("hex")));
stream.on("error", reject);
});
}
async function download(url, dest) {
const res = await fetch(url, { redirect: "follow" });
if (!res.ok) {
throw new Error(`HTTP ${res.status} ${res.statusText} for ${url}`);
}
if (!res.body) {
throw new Error(`no response body for ${url}`);
}
const fileStream = createWriteStream(dest);
await new Promise((resolve, reject) => {
const body = Readable.fromWeb(res.body);
body.pipe(fileStream);
body.on("error", reject);
fileStream.on("finish", resolve);
fileStream.on("error", reject);
});
}
let failed = false;
for (const asset of ASSETS) {
const target = path.join(ASSET_DIR, asset.file);
if (existsSync(target)) {
const actual = await sha256File(target);
if (actual === asset.sha256) {
console.log(`okay ${asset.file} (up to date)`);
continue;
}
console.error(
`error ${asset.file}: sha256 mismatch (expected ${asset.sha256}, got ${actual}); delete it and rerun`,
);
failed = true;
continue;
}
const url = `https://github.com/${REPO}/releases/download/${RELEASE_TAG}/${asset.file}`;
console.log(`get ${asset.file} <- ${url}`);
await mkdir(path.dirname(target), { recursive: true });
const tmp = target + ".part";
try {
await download(url, tmp);
const actual = await sha256File(tmp);
if (actual !== asset.sha256) {
console.error(
`error ${asset.file}: downloaded sha256 mismatch (expected ${asset.sha256}, got ${actual})`,
);
failed = true;
if (existsSync(tmp)) {
unlinkSync(tmp);
}
continue;
}
await rename(tmp, target);
console.log(`okay ${asset.file} (downloaded)`);
} catch (error) {
console.error(`error ${asset.file}: ${error.message}`);
failed = true;
if (existsSync(tmp)) {
unlinkSync(tmp);
}
}
}
process.exit(failed ? 1 : 0);

View file

@ -9,13 +9,13 @@ APP_PATH="$1"
BUNDLE_ID="com.emerald.legacy"
if [ -z "$APP_PATH" ]; then
echo "Usage: $0 <path-to-app-bundle>"
exit 1
echo "Usage: $0 <path-to-app-bundle>"
exit 1
fi
if [ ! -d "$APP_PATH" ]; then
echo "Error: App bundle not found at $APP_PATH"
exit 1
echo "Error: App bundle not found at $APP_PATH"
exit 1
fi
echo "Fixing macOS quarantine for: $APP_PATH"
@ -25,8 +25,8 @@ xattr -cr "$APP_PATH"
# Add basic code signing (ad-hoc signature)
codesign --force --deep --sign - "$APP_PATH" 2>/dev/null || {
echo "Warning: Code signing failed, but quarantine removal should work"
echo "Warning: Code signing failed, but quarantine removal should work"
}
echo "✅ macOS quarantine fix completed"
echo "macOS quarantine fix completed"
echo "The app should now launch without 'damaged' error"

View file

@ -0,0 +1,81 @@
import argparse
import re
import os
def update_stable(version, intel_hash, arm_hash, linux_hash):
cask_path = "Casks/lce-emerald-launcher.rb"
formula_path = "Formula/lce-emerald-launcher.rb"
# Update Cask
if os.path.exists(cask_path):
with open(cask_path, 'r') as f:
content = f.read()
# Replace version
content = re.sub(r'^\s*version\s+"[^"]+"', f' version "{version}"', content, flags=re.MULTILINE)
# Replace sha256 block
content = re.sub(r'sha256\s+intel:\s+"[a-f0-9]+",\r?\n\s+arm:\s+"[a-f0-9]+"',
f'sha256 intel: "{intel_hash}",\n arm: "{arm_hash}"',
content)
with open(cask_path, 'w') as f:
f.write(content)
print(f"Updated {cask_path}")
else:
print(f"Error: {cask_path} not found")
# Update Formula
if os.path.exists(formula_path):
with open(formula_path, 'r') as f:
content = f.read()
# Replace version
content = re.sub(r'^\s*version\s+"[^"]+"', f' version "{version}"', content, flags=re.MULTILINE)
# Replace sha256
content = re.sub(r'^\s*sha256\s+"[a-f0-9]+"', f' sha256 "{linux_hash}"', content, flags=re.MULTILINE)
with open(formula_path, 'w') as f:
f.write(content)
print(f"Updated {formula_path}")
else:
print(f"Error: {formula_path} not found")
def update_experimental(version, intel_hash, arm_hash):
cask_path = "Casks/lce-emerald-launcher-experimental.rb"
if os.path.exists(cask_path):
with open(cask_path, 'r') as f:
content = f.read()
# Replace version
content = re.sub(r'^\s*version\s+"[^"]+"', f' version "{version}"', content, flags=re.MULTILINE)
# Replace sha256 block
content = re.sub(r'sha256\s+intel:\s+"[a-f0-9]+",\r?\n\s+arm:\s+"[a-f0-9]+"',
f'sha256 intel: "{intel_hash}",\n arm: "{arm_hash}"',
content)
with open(cask_path, 'w') as f:
f.write(content)
print(f"Updated {cask_path}")
else:
print(f"Error: {cask_path} not found")
def main():
parser = argparse.ArgumentParser(description="Update Homebrew Formula/Cask files")
parser.add_argument("--type", choices=["stable" ], required=True)
parser.add_argument("--version", required=True)
parser.add_argument("--intel-hash", required=True)
parser.add_argument("--arm-hash", required=True)
parser.add_argument("--linux-hash")
args = parser.parse_args()
if args.type == "stable":
if not args.linux_hash:
raise ValueError("--linux-hash is required for stable update")
update_stable(args.version, args.intel_hash, args.arm_hash, args.linux_hash)
elif args.type == "experimental":
update_experimental(args.version, args.intel_hash, args.arm_hash)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,50 @@
#!/usr/bin/env python3
import argparse
import re
import sys
def update_flake(version: str, src_hash: str) -> None:
path = "flake.nix"
with open(path, "r", encoding="utf-8") as f:
content = f.read()
new_content, n_version = re.subn(
r'stableVersion = "[^"]*";',
f'stableVersion = "{version}";',
content,
count=1,
)
new_content, n_hash = re.subn(
r'stableSrcHash = "[^"]*";',
f'stableSrcHash = "{src_hash}";',
new_content,
count=1,
)
if n_version != 1 or n_hash != 1:
print(
f"Error: expected to update stableVersion and stableSrcHash once each "
f"(got version={n_version}, hash={n_hash})",
file=sys.stderr,
)
sys.exit(1)
with open(path, "w", encoding="utf-8") as f:
f.write(new_content)
print(f"Updated {path}: stableVersion={version} stableSrcHash={src_hash}")
def main() -> None:
parser = argparse.ArgumentParser(
description="Update flake.nix stableVersion and stableSrcHash"
)
parser.add_argument("--version", required=True, help="Stable version without v prefix")
parser.add_argument("--src-hash", required=True, help="SRI sha256 of the release source")
args = parser.parse_args()
update_flake(args.version, args.src_hash)
if __name__ == "__main__":
main()

270
src-tauri/Cargo.lock generated
View file

@ -635,7 +635,7 @@ dependencies = [
"bitflags 2.11.0",
"core-foundation 0.10.1",
"core-graphics-types",
"foreign-types 0.5.0",
"foreign-types",
"libc",
]
@ -1043,7 +1043,7 @@ checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
[[package]]
name = "emerald-legacy-launcher"
version = "1.4.0"
version = "1.6.1"
dependencies = [
"base64 0.21.7",
"byteorder",
@ -1051,6 +1051,7 @@ dependencies = [
"futures-util",
"http 1.4.0",
"image 0.24.9",
"jni",
"libc",
"lzxd",
"once_cell",
@ -1074,6 +1075,7 @@ dependencies = [
"tokio-util",
"url",
"uuid 1.23.0",
"wry",
]
[[package]]
@ -1264,15 +1266,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared 0.1.1",
]
[[package]]
name = "foreign-types"
version = "0.5.0"
@ -1280,7 +1273,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
dependencies = [
"foreign-types-macros",
"foreign-types-shared 0.3.1",
"foreign-types-shared",
]
[[package]]
@ -1294,12 +1287,6 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "foreign-types-shared"
version = "0.3.1"
@ -1926,6 +1913,12 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "http-range"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
[[package]]
name = "httparse"
version = "1.10.1"
@ -1982,6 +1975,20 @@ dependencies = [
"want",
]
[[package]]
name = "hyper-rustls"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
dependencies = [
"futures-util",
"http 0.2.12",
"hyper 0.14.32",
"rustls 0.21.12",
"tokio",
"tokio-rustls 0.24.1",
]
[[package]]
name = "hyper-rustls"
version = "0.27.9"
@ -1991,25 +1998,12 @@ dependencies = [
"http 1.4.0",
"hyper 1.9.0",
"hyper-util",
"rustls",
"rustls 0.23.40",
"tokio",
"tokio-rustls",
"tokio-rustls 0.26.4",
"tower-service",
]
[[package]]
name = "hyper-tls"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes",
"hyper 0.14.32",
"native-tls",
"tokio",
"tokio-native-tls",
]
[[package]]
name = "hyper-util"
version = "0.1.20"
@ -2027,7 +2021,7 @@ dependencies = [
"libc",
"percent-encoding",
"pin-project-lite",
"socket2 0.6.3",
"socket2 0.5.10",
"tokio",
"tower-service",
"tracing",
@ -2686,23 +2680,6 @@ dependencies = [
"windows-sys 0.60.2",
]
[[package]]
name = "native-tls"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2"
dependencies = [
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]]
name = "ndk"
version = "0.9.0"
@ -2980,50 +2957,12 @@ dependencies = [
"pathdiff",
]
[[package]]
name = "openssl"
version = "0.10.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf"
dependencies = [
"bitflags 2.11.0",
"cfg-if",
"foreign-types 0.3.2",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "openssl-probe"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
[[package]]
name = "openssl-sys"
version = "0.9.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "option-ext"
version = "0.2.0"
@ -3781,15 +3720,15 @@ dependencies = [
"http 0.2.12",
"http-body 0.4.6",
"hyper 0.14.32",
"hyper-tls",
"hyper-rustls 0.24.2",
"ipnet",
"js-sys",
"log",
"mime",
"native-tls",
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls 0.21.12",
"rustls-pemfile",
"serde",
"serde_json",
@ -3797,7 +3736,7 @@ dependencies = [
"sync_wrapper 0.1.2",
"system-configuration",
"tokio",
"tokio-native-tls",
"tokio-rustls 0.24.1",
"tokio-util",
"tower-service",
"url",
@ -3805,6 +3744,7 @@ dependencies = [
"wasm-bindgen-futures",
"wasm-streams 0.4.2",
"web-sys",
"webpki-roots 0.25.4",
"winreg 0.50.0",
]
@ -3822,20 +3762,20 @@ dependencies = [
"http-body 1.0.1",
"http-body-util",
"hyper 1.9.0",
"hyper-rustls",
"hyper-rustls 0.27.9",
"hyper-util",
"js-sys",
"log",
"percent-encoding",
"pin-project-lite",
"rustls",
"rustls 0.23.40",
"rustls-pki-types",
"rustls-platform-verifier",
"serde",
"serde_json",
"sync_wrapper 1.0.2",
"tokio",
"tokio-rustls",
"tokio-rustls 0.26.4",
"tokio-util",
"tower",
"tower-http",
@ -3936,6 +3876,32 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "rustls"
version = "0.21.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
dependencies = [
"log",
"ring",
"rustls-webpki 0.101.7",
"sct",
]
[[package]]
name = "rustls"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
dependencies = [
"log",
"ring",
"rustls-pki-types",
"rustls-webpki 0.102.8",
"subtle",
"zeroize",
]
[[package]]
name = "rustls"
version = "0.23.40"
@ -3945,7 +3911,7 @@ dependencies = [
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"rustls-webpki 0.103.13",
"subtle",
"zeroize",
]
@ -3991,10 +3957,10 @@ dependencies = [
"jni",
"log",
"once_cell",
"rustls",
"rustls 0.23.40",
"rustls-native-certs",
"rustls-platform-verifier-android",
"rustls-webpki",
"rustls-webpki 0.103.13",
"security-framework",
"security-framework-sys",
"webpki-root-certs",
@ -4007,6 +3973,27 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
[[package]]
name = "rustls-webpki"
version = "0.101.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "rustls-webpki"
version = "0.102.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "rustls-webpki"
version = "0.103.13"
@ -4111,6 +4098,16 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sct"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "security-framework"
version = "3.7.0"
@ -4708,9 +4705,9 @@ dependencies = [
[[package]]
name = "tao-macros"
version = "0.1.3"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd"
checksum = "5f7eeb6d99155545da6150a1795945f16ac9c178deb2a5f2e74d776107bd5849"
dependencies = [
"proc-macro2",
"quote",
@ -4751,6 +4748,7 @@ dependencies = [
"gtk",
"heck 0.5.0",
"http 1.4.0",
"http-range",
"image 0.25.10",
"jni",
"libc",
@ -4983,7 +4981,7 @@ dependencies = [
"osakit",
"percent-encoding",
"reqwest 0.13.2",
"rustls",
"rustls 0.23.40",
"semver",
"serde",
"serde_json",
@ -5263,12 +5261,23 @@ dependencies = [
]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"
name = "tokio-rustls"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [
"native-tls",
"rustls 0.21.12",
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
dependencies = [
"rustls 0.22.4",
"rustls-pki-types",
"tokio",
]
@ -5278,7 +5287,7 @@ version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
dependencies = [
"rustls",
"rustls 0.23.40",
"tokio",
]
@ -5290,10 +5299,12 @@ checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38"
dependencies = [
"futures-util",
"log",
"native-tls",
"rustls 0.22.4",
"rustls-pki-types",
"tokio",
"tokio-native-tls",
"tokio-rustls 0.25.0",
"tungstenite",
"webpki-roots 0.26.11",
]
[[package]]
@ -5402,18 +5413,18 @@ dependencies = [
[[package]]
name = "toml_parser"
version = "1.1.2+spec-1.1.0"
version = "1.1.3+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
dependencies = [
"winnow 1.0.1",
]
[[package]]
name = "toml_writer"
version = "1.1.1+spec-1.1.0"
version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
[[package]]
name = "tower"
@ -5531,8 +5542,9 @@ dependencies = [
"http 1.4.0",
"httparse",
"log",
"native-tls",
"rand 0.8.5",
"rustls 0.22.4",
"rustls-pki-types",
"sha1",
"thiserror 1.0.69",
"url",
@ -5691,12 +5703,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vec_map"
version = "0.8.2"
@ -6034,6 +6040,30 @@ dependencies = [
"rustls-pki-types",
]
[[package]]
name = "webpki-roots"
version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]]
name = "webpki-roots"
version = "0.26.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
dependencies = [
"webpki-roots 1.0.9",
]
[[package]]
name = "webpki-roots"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "webview2-com"
version = "0.38.2"
@ -6098,7 +6128,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.48.0",
]
[[package]]

View file

@ -1,9 +1,9 @@
[package]
name = "emerald-legacy-launcher"
version = "1.4.0"
version = "1.6.1"
description = "A FOSS, cross-platform launcher for Minecraft Legacy Edition"
authors = ["Emerald Team"]
edition = "2021"
edition = "2024"
[lib]
name = "emerald_lib"
@ -13,22 +13,20 @@ crate-type = ["staticlib", "cdylib", "rlib"]
tauri-build = { version = "2", features = [] }
[dependencies]
tauri = { version = "2", features = [ "tray-icon", "image-png"] }
tauri = { version = "2", features = ["protocol-asset", "tray-icon", "image-png"] }
tauri-plugin-opener = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tauri-plugin-gamepad = "0.0.5"
tauri-plugin-drpc = "*"
reqwest = { version = "0.11", features = ["stream", "json"] }
reqwest = { version = "0.11", default-features = false, features = ["stream", "json", "rustls-tls-webpki-roots"] }
tokio = { version = "1", features = ["full"] }
futures-util = "0.3"
tokio-util = { version = "0.7.18", features = ["rt"] }
base64 = "0.21"
tokio-tungstenite = { version = "0.21", default-features = false, features = ["connect", "native-tls"] }
tokio-tungstenite = { version = "0.21", default-features = false, features = ["connect", "rustls-tls-webpki-roots"] }
url = "2"
rand = "0.8"
http = "1"
rfd = "0.15"
libc = "0.2"
image = "0.24"
steam_shortcuts_util = "1.1.8"
@ -39,7 +37,18 @@ once_cell = "1"
lzxd = "0.2.6"
uuid = { version = "1", features = ["v4"] }
tauri-plugin-deep-link = "2"
tauri-plugin-single-instance = "2"
[target.'cfg(target_os = "android")'.dependencies]
wry = { version = "0.54", default-features = false }
jni = "0.21"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-updater = "2"
tauri-plugin-drpc = "*"
tauri-plugin-single-instance = "2"
rfd = "0.15"
#neo: enable portable appimages. revert back to crates.io once the PR is merged
#[patch.crates-io]
#tauri = { git = "https://github.com/tauri-apps/tauri", branch = "feat/truly-portable-appimage" }
#end neo

View file

@ -1,9 +1,8 @@
{
"identifier": "default",
"description": "Default permissions",
"windows": [
"main"
],
"platforms": ["windows", "linux", "macOS"],
"windows": ["main"],
"permissions": [
"core:default",
"core:app:default",
@ -14,7 +13,13 @@
"core:resources:default",
"core:tray:default",
"core:webview:default",
"core:webview:allow-create-webview-window",
"core:window:default",
"core:window:allow-set-decorations",
"core:window:allow-is-fullscreen",
"core:window:allow-minimize",
"core:window:allow-toggle-maximize",
"core:window:allow-close",
"opener:default",
"gamepad:default",
"drpc:default",
@ -36,6 +41,7 @@
]
},
"process:default",
"deep-link:default"
"deep-link:default",
"core:window:allow-start-dragging"
]
}
}

View file

@ -1,16 +1,11 @@
{
"identifier": "desktop-capability",
"platforms": [
"macOS",
"windows",
"linux"
],
"windows": [
"main"
],
"platforms": ["macOS", "windows", "linux"],
"windows": ["main"],
"permissions": [
"updater:default",
"process:default",
"deep-link:default"
"deep-link:default",
"core:window:allow-start-dragging"
]
}
}

View file

@ -0,0 +1,29 @@
{
"identifier": "mobile-capability",
"description": "Mobile permissions",
"platforms": ["android", "iOS"],
"windows": ["main"],
"permissions": [
"core:default",
"core:app:default",
"core:event:default",
"core:image:default",
"core:path:default",
"core:resources:default",
"core:webview:default",
"core:webview:allow-create-webview-window",
"core:window:default",
"opener:default",
{
"identifier": "opener:allow-open-path",
"allow": [
{
"path": "**"
}
]
},
"gamepad:default",
"process:default",
"deep-link:default"
]
}

View file

@ -0,0 +1,12 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false

20
src-tauri/gen/android/.gitignore vendored Normal file
View file

@ -0,0 +1,20 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
build
/captures
.externalNativeBuild
.cxx
local.properties
key.properties
release.keystore
/.tauri
/tauri.settings.gradle

6
src-tauri/gen/android/app/.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
/src/main/**/generated
/src/main/jniLibs/**/*.so
/src/main/assets/tauri.conf.json
/tauri.build.gradle.kts
/proguard-tauri.pro
/tauri.properties

View file

@ -0,0 +1,93 @@
import java.util.Properties
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("rust")
}
val tauriProperties = Properties().apply {
val propFile = file("tauri.properties")
if (propFile.exists()) {
propFile.inputStream().use { load(it) }
}
}
val keystoreProperties = Properties().apply {
val propFile = file("key.properties")
if (propFile.exists()) {
propFile.inputStream().use { load(it) }
}
}
android {
signingConfigs {
create("release") {
keyAlias = keystoreProperties.getProperty("keyAlias")
keyPassword = keystoreProperties.getProperty("keyPassword")
storeFile = file(keystoreProperties.getProperty("storeFile"))
storePassword = keystoreProperties.getProperty("storePassword")
}
}
compileSdk = 36
namespace = "com.emerald.legacy"
defaultConfig {
manifestPlaceholders["usesCleartextTraffic"] = "false"
applicationId = "com.emerald.legacy"
minSdk = 26
targetSdk = 28
versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt()
versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0")
}
buildTypes {
getByName("debug") {
manifestPlaceholders["usesCleartextTraffic"] = "true"
isDebuggable = true
isJniDebuggable = true
isMinifyEnabled = false
packaging { jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so")
jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so")
jniLibs.keepDebugSymbols.add("*/x86/*.so")
jniLibs.keepDebugSymbols.add("*/x86_64/*.so")
}
}
getByName("release") {
signingConfig = signingConfigs.getByName("release")
isMinifyEnabled = true
proguardFiles(
*fileTree(".") { include("**/*.pro") }
.plus(getDefaultProguardFile("proguard-android-optimize.txt"))
.toList().toTypedArray()
)
}
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
buildConfig = true
}
lint {
disable += "ExpiredTargetSdkVersion"
}
packaging {
resources.excludes += "lib/*/jni/**"
}
}
rust {
rootDirRel = "../../../"
}
dependencies {
implementation(project(":diamondruntime"))
implementation("androidx.webkit:webkit:1.14.0")
implementation("androidx.appcompat:appcompat:1.7.1")
implementation("androidx.activity:activity-ktx:1.10.1")
implementation("com.google.android.material:material:1.12.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.4")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0")
}
apply(from = "tauri.build.gradle.kts")

View file

@ -0,0 +1,19 @@
-keep class dev.lcehub.emerald.** { *; }
-keepclassmembers class dev.lcehub.emerald.** { *; }
-dontwarn dev.lcehub.emerald.**
-keep class com.github.luben.zstd.** { *; }
-keepclassmembers class com.github.luben.zstd.** { *; }
-dontwarn com.github.luben.zstd.**
-keep class com.emerald.legacy.TauriActivity {
public <methods>;
public <fields>;
}
-keep class com.emerald.legacy.MainActivity {
public <methods>;
}
-dontwarn com.android.org.conscrypt.SSLParametersImpl
-dontwarn org.apache.harmony.xnet.provider.jsse.SSLParametersImpl
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.BCSSLSocket
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<!-- AndroidTV support -->
<uses-feature android:name="android.software.leanback" android:required="false" />
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.emerald_legacy_launcher"
android:extractNativeLibs="true"
android:usesCleartextTraffic="${usesCleartextTraffic}"
tools:replace="android:label,android:theme">
<activity
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
android:launchMode="singleTask"
android:screenOrientation="sensorLandscape"
android:label="@string/main_activity_title"
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<!-- AndroidTV support -->
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
<!-- DEEP LINK PLUGIN. AUTO-GENERATED. DO NOT REMOVE. -->
<!-- DEEP LINK PLUGIN. AUTO-GENERATED. DO NOT REMOVE. -->
</activity>
<activity
android:name=".LceAuthActivity"
android:exported="false"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>

View file

@ -0,0 +1,124 @@
package com.emerald.legacy
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.os.Message
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.webkit.WebResourceRequest
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.FrameLayout
import android.widget.ProgressBar
import androidx.appcompat.app.AppCompatActivity
class LceAuthActivity : AppCompatActivity() {
private lateinit var webView: WebView
private lateinit var progressBar: ProgressBar
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val authUrl = intent.getStringExtra("authUrl")
?: "https://mclegacyedition.xyz/internal/auth?appId=emerald_launcher"
progressBar = ProgressBar(this).apply {
layoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
webView = WebView(this).apply {
settings.javaScriptEnabled = true
settings.domStorageEnabled = true
settings.databaseEnabled = true
settings.loadWithOverviewMode = true
settings.useWideViewPort = true
settings.setSupportMultipleWindows(true)
settings.javaScriptCanOpenWindowsAutomatically = true
webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean = handleAuthUrl(url)
override fun shouldOverrideUrlLoading(
view: WebView,
request: WebResourceRequest
): Boolean = handleAuthUrl(request.url.toString())
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
handleAuthUrl(url)
}
override fun onPageFinished(view: WebView, url: String) {
progressBar.visibility = View.GONE
}
}
webChromeClient = object : android.webkit.WebChromeClient() {
override fun onCreateWindow(
view: WebView,
isDialog: Boolean,
isUserGesture: Boolean,
resultMsg: Message
): Boolean {
val transport = resultMsg.obj as WebView.WebViewTransport
transport.webView = webView
resultMsg.sendToTarget()
return true
}
}
}
val root = FrameLayout(this)
root.addView(
webView,
FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
)
val progressFrame = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
progressFrame.gravity = Gravity.CENTER
root.addView(progressBar, progressFrame)
setContentView(root)
webView.loadUrl(authUrl)
}
private fun handleAuthUrl(url: String): Boolean {
val uri = try {
Uri.parse(url)
} catch (e: Exception) {
return false
}
if (uri.scheme == "emerald" && uri.host == "lceonline") {
val token = uri.getQueryParameter("token")
if (token != null) {
setResult(RESULT_OK, Intent().putExtra("lceAuthToken", token))
} else {
setResult(RESULT_CANCELED)
}
finish()
return true
}
return false
}
@Deprecated("Deprecated in Java")
override fun onBackPressed() {
if (webView.canGoBack()) {
webView.goBack()
} else {
setResult(RESULT_CANCELED)
super.onBackPressed()
}
}
override fun onDestroy() {
(webView.parent as? ViewGroup)?.removeView(webView)
webView.destroy()
super.onDestroy()
}
}

View file

@ -0,0 +1,37 @@
package com.emerald.legacy
import android.app.Activity
import android.content.Intent
import androidx.activity.result.ActivityResult
import app.tauri.annotation.ActivityCallback
import app.tauri.annotation.Command
import app.tauri.annotation.TauriPlugin
import app.tauri.plugin.Invoke
import app.tauri.plugin.Plugin
@TauriPlugin
class LceAuthPlugin(private val activity: Activity) : Plugin(activity) {
@Command
fun startAuth(invoke: Invoke) {
val intent = Intent(activity, LceAuthActivity::class.java)
intent.putExtra(
"authUrl",
"https://mclegacyedition.xyz/internal/auth?appId=emerald_launcher"
)
startActivityForResult(invoke, intent, "authResult")
}
@ActivityCallback
fun authResult(invoke: Invoke, result: ActivityResult) {
val token = if (result.resultCode == Activity.RESULT_OK) {
result.data?.getStringExtra("lceAuthToken")
} else {
null
}
if (!token.isNullOrEmpty()) {
invoke.resolveObject(token)
} else {
invoke.reject("Authentication cancelled or failed")
}
}
}

View file

@ -0,0 +1,28 @@
package com.emerald.legacy
import android.os.Bundle
import android.webkit.WebView
import androidx.activity.enableEdgeToEdge
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
class MainActivity : TauriActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
WindowCompat.getInsetsController(window, window.decorView).apply {
systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
hide(WindowInsetsCompat.Type.statusBars() or WindowInsetsCompat.Type.navigationBars())
}
}
override fun onWebViewCreate(webView: WebView) {
super.onWebViewCreate(webView)
webView.settings.useWideViewPort = true
webView.settings.loadWithOverviewMode = true
webView.settings.setSupportZoom(false)
webView.settings.builtInZoomControls = false
webView.settings.displayZoomControls = false
}
}

View file

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#00591C"
android:pathData="M0,0h108v108h-108z" />
</vector>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Some files were not shown because too many files have changed in this diff Show more