Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
75864fd
Upgrade build to Java 17
Magi1053 Jun 9, 2026
d01e9a0
Move memory and Java settings from global config to per-instance
Magi1053 Jun 9, 2026
f56a4ef
Add managed Game Runtime install from Mojang manifest
Magi1053 Jun 9, 2026
aefedc6
Add cross-platform native installer packaging and harden bootstrap st…
Magi1053 Jun 9, 2026
5c03993
Add Microsoft device-code sign-in and refresh account picker
Magi1053 Jun 9, 2026
c61e69f
Replace news panel with JavaFX WebView for embedded page rendering
Magi1053 Jun 9, 2026
1402bf6
Make feature exclude patterns control install conditions
Magi1053 Jun 9, 2026
810e11a
Refactor ProcessorEntry install to respect cache before queueing proc…
Magi1053 Jun 9, 2026
42c1c29
Add modpack memory requirements with insufficient-memory warning
Magi1053 Jun 9, 2026
d12aead
Preserve creator-defined optional feature ordering
Magi1053 Jun 9, 2026
f40d680
Improve instance list readability with larger rows
Magi1053 Jun 9, 2026
2ffb49e
Reload instances when game key changes on options save
Magi1053 Jun 9, 2026
f8989f6
Support per-instance news URLs
Magi1053 Jun 9, 2026
2cfb2a6
Ignore missing launcher registry key
Magi1053 Jun 10, 2026
a72da99
Overhaul launcher look-and-feel with FlatLaf
Magi1053 Jun 10, 2026
203c6e4
Remember last launched instance and improve instance selection handling
Magi1053 Jun 10, 2026
a5e4254
Replace OpenJFX news panel with platform-native embedded browser
Magi1053 Jul 18, 2026
5856aee
Ship browser runtime via cross-platform native installers
Magi1053 Jul 18, 2026
29e2d97
Rewrite game console with deferred show, bounded buffer, and level co…
Magi1053 Jul 21, 2026
6c6dc70
Redesign instance list with icons, status rows, and borders
Magi1053 Jul 23, 2026
209cea0
Add offline accounts with switcher and manage-accounts UI
Magi1053 Jul 23, 2026
f39d87a
Improve launch memory checks, update verification, and downloads.
Magi1053 Jul 23, 2026
2ca7b11
Add CurseForge CDN download handlers so mod URL fetches authenticate.
Magi1053 Jul 26, 2026
0929c2b
Skip Forge/NeoForge processors when their outputs already exist.
Magi1053 Jul 26, 2026
42e4829
Replace SWT/WKWebView news browser with weblite.
Magi1053 Jul 28, 2026
8b1977d
Fix native jpackage installers and seed bundled launcher payloads.
Magi1053 Jul 28, 2026
0ce796e
Simplify account UI and centralize shared Swing icons.
Magi1053 Jul 28, 2026
df60855
Polish launcher, bootstrap, and creator-tools cleanup.
Magi1053 Jul 28, 2026
958c49b
Refresh README screenshots and docs for light/dark UI, accounts, and …
Magi1053 Jul 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
gradle/*.sh text eol=lf
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ pom.xml.next
release.properties


### Flatpak ###
.flatpak-builder/

### Gradle ###
.gradle
build/
Expand Down Expand Up @@ -155,4 +158,5 @@ $RECYCLE.BIN/
/libraries
/versions
/accounts.dat
/config.json
/config.json
/run
45 changes: 36 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
SKCraft Launcher
================

![Now with 1.13+ support!](readme/now_with_support.png)

[![Liberapay](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/offbeatwitch/donate)

*If you rely on this software, please consider [donating](https://liberapay.com/offbeatwitch/donate) to help keep it maintained!*
Expand All @@ -14,6 +12,7 @@ Need to make it easy for people to install and play your modpack? The SKCraft La
:heavy_check_mark: You don't want to depend on anyone else
:heavy_check_mark: There's several modpacks you want to provide as a set
:heavy_check_mark: You want to provide 'hidden' modpacks that are only accessible by a secret code
:heavy_check_mark: You want Windows / Linux / macOS installers for distributing the launcher itself

It may not be for you if:

Expand All @@ -27,7 +26,9 @@ Creating modpacks with this launcher is super easy: :one: only drag & drop of co

The launcher has all standard features that you'd expect like :one: resume of incomplete downloads, :two: incremental updates, and :three: file deduplication (saves disk space when you have files duplicated between updates or modpacks).

LiteLoader, Forge, Fabric, and custom .jar mods are supported. You can put resource packs in, or really even random mod files that don't go in the configs or mods folder.
LiteLoader, Forge, Fabric, NeoForge, and custom .jar mods are supported. You can put resource packs in, or really even random mod files that don't go in the configs or mods folder.

Light and dark modes are supported. News is shown in an embedded browser and can be set per instance. Players can sign in with Microsoft or use offline profiles. You can build native installers for Windows, Linux, and macOS — see [launcher-bootstrap/installer/README.md](launcher-bootstrap/installer/README.md).

You do need some sort of website, but it does **not** need anything complicated like PHP.

Expand All @@ -39,7 +40,29 @@ Best of all, our launcher is Minecraft's oldest surviving modpack launcher platf

You'll probably need a few hours to get everything working. Fortunately, we have a [detailed wiki](https://github.com/SKCraft/Launcher/wiki) with screenshots for every step, intended for someone with minimal technical experience.

For your evaluation, there are sample files provided that you will be able to upload to your site to see if it even works for you.
For your evaluation, there are [sample files](sample-files/) provided that you will be able to upload to your site to see if it even works for you.

## Building

Build with a Java 17 JDK. The Gradle wrapper should also be run by Java 17:

```
gradlew.bat build
```

For native installer packaging:

- Windows: `gradlew.bat :launcher-bootstrap:packageWindows -Pversion=...` (builds `Setup.exe` with NSIS)
- Linux: `./gradlew :launcher-bootstrap:packageLinux -Pversion=...` (AppImage + Flatpak + DEB)
- Linux from Windows: `build.bat --linux` (WSL hybrid; AppImage + Flatpak + DEB)
- Linux from Windows + WSL: `gradlew.bat :launcher-bootstrap:packageLinuxWsl -Pversion=... -PwslDistro=Ubuntu`
- macOS: `./gradlew :launcher-bootstrap:packageMac -Pversion=...` (builds `.dmg` and `.pkg`)

All of those start from the same staged runtime image:

```
gradlew.bat :launcher-bootstrap:assembleAppImage
```

## Pretty Pictures

Expand All @@ -62,24 +85,28 @@ LiteLoader and Forge installers, or Fabric Loader, go into the *loaders* folder.

### More Pictures

There's a dark version of the launcher (personally not the creator's cup of tea) that you can use:
The launcher comes in light and dark modes:

![Light Mode](readme/launcher-light.png)

![Skinned](readme/launcher_skinned.png)
![Dark Mode](readme/launcher-dark.png)

Users can select optional mods/features:

![Optional Features](readme/features.png)

The launcher can update itself and you can use it in portable mode too.
Accounts support Microsoft sign-in and offline profiles:

### The Light Theme
![Accounts](readme/accounts.png)

![Main Launcher](readme/launcher.png)
You can set Java, memory, and JVM arguments per instance:

![Options](readme/options.png)

![Console](readme/log.png)

The launcher can update itself and you can use it in portable mode too.

## Contributing

Pull requests can be submitted on GitHub, but we will accept them at our discretion. Please note that your code must follow Oracle's Java Code Conventions.
Expand Down
88 changes: 85 additions & 3 deletions build.bat
Original file line number Diff line number Diff line change
@@ -1,3 +1,85 @@
@echo off
call gradlew clean build
pause
@echo off
setlocal EnableExtensions EnableDelayedExpansion

set "BUILD_INSTALLER=1"
set "BUILD_WINDOWS=1"
set "BUILD_LINUX=1"
set "RESOLVE_ARGS="
set "ROOT=%~dp0"

:parse
if "%~1"=="" goto parsed
set "A=%~1"
if /i "!A:~0,2!"=="--" set "A=!A:~2!"
if /i "!A!"=="no-installer" set "BUILD_INSTALLER=0"
if /i "!A!"=="windows" (set "BUILD_WINDOWS=1" & set "BUILD_LINUX=0")
if /i "!A!"=="linux" (set "BUILD_WINDOWS=0" & set "BUILD_LINUX=1")
shift
goto parse
:parsed

if "%BUILD_WINDOWS%%BUILD_LINUX%"=="00" (
echo ERROR: Nothing to package.
goto :fail
)

if "%BUILD_LINUX%"=="1" if "%BUILD_INSTALLER%"=="1" (
where wsl >nul 2>&1
if errorlevel 1 (
echo ERROR: Linux packaging requires WSL.
goto :fail
)
wsl -- echo WSL_OK >nul 2>&1
if errorlevel 1 (
echo ERROR: Linux packaging requires a working WSL distro.
goto :fail
)
)
if "%BUILD_LINUX%"=="1" if "%BUILD_INSTALLER%"=="1" (
set "WSL_MISSING="
for %%T in (java curl file fakeroot flatpak flatpak-builder) do (
wsl -- bash -lc "type %%T" >nul 2>&1
if errorlevel 1 set "WSL_MISSING=!WSL_MISSING! %%T"
)
if defined WSL_MISSING (
set "INSTALL_WSL_DEPS="
set /p "INSTALL_WSL_DEPS=Missing WSL packaging tools:!WSL_MISSING!. Install now? [y/N] "
if /i not "!INSTALL_WSL_DEPS!"=="y" (
echo Linux packaging skipped because dependency installation was declined.
set "SKIP_LINUX_PACKAGE=1"
) else (
echo Installing WSL packaging dependencies...
wsl -u root -- apt-get update
if errorlevel 1 goto :fail
wsl -u root -- apt-get install -y openjdk-17-jdk curl file fakeroot flatpak flatpak-builder
if errorlevel 1 goto :fail
)
)
if "!SKIP_LINUX_PACKAGE!"=="1" (
set "BUILD_LINUX=0"
)
)

if "%BUILD_INSTALLER%"=="0" (
set "RESOLVE_ARGS=--no-installer"
) else if "%BUILD_WINDOWS%"=="0" (
set "RESOLVE_ARGS=--no-nsis"
)
call "%ROOT%gradle\resolve-build-env.bat" %RESOLVE_ARGS%
if errorlevel 1 goto :fail
echo Using JAVA_HOME=%JAVA_HOME%

set "TASKS="
if "%BUILD_INSTALLER%"=="1" (
if "%BUILD_WINDOWS%"=="1" set "TASKS=:launcher-bootstrap:packageWindows"
if "%BUILD_LINUX%"=="1" (
if defined TASKS (set "TASKS=!TASKS! :launcher-bootstrap:packageLinuxWsl") else set "TASKS=:launcher-bootstrap:packageLinuxWsl"
)
echo Packaging tasks: !TASKS!
)

call "%ROOT%gradlew" clean build %TASKS%
exit /b %ERRORLEVEL%

:fail
exit /b 1
43 changes: 7 additions & 36 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
plugins {
id "com.github.johnrengelman.shadow" version "7.1.2"
id 'io.freefair.lombok' version '5.3.0'
id "com.gradleup.shadow" version "8.3.6"
id 'io.freefair.lombok' version '8.13.1'
}

println """
Expand All @@ -19,7 +19,7 @@ subprojects {

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(8))
languageVersion.set(JavaLanguageVersion.of(17))
}
}

Expand All @@ -28,7 +28,6 @@ subprojects {
}

if (JavaVersion.current().isJava8Compatible()) {
// Java 8 turns on doclint which we fail
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
}
Expand All @@ -38,38 +37,10 @@ subprojects {
workingDir = new File(rootDir, "run/")
workingDir.mkdirs()
}

// Work around gradle shadow bug
// see https://github.com/johnrengelman/shadow/issues/713
afterEvaluate {
startScripts {
dependsOn(shadowJar)
}

distTar {
dependsOn(shadowJar)
}

distZip {
dependsOn(shadowJar)
}

startShadowScripts {
dependsOn(jar)
}

shadowDistTar {
dependsOn(jar)
}

shadowDistZip {
dependsOn(jar)
}
}
}

task clean {
subprojects {
rootProject.clean.dependsOn tasks.matching { it.name == "clean" }
}
tasks.register('package') {
group = 'distribution'
description = 'Build native launcher installer for the current platform.'
dependsOn ':launcher-bootstrap:package'
}
73 changes: 72 additions & 1 deletion build.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,3 +1,74 @@
#!/bin/sh
BUILD_INSTALLER=1
if [ "$1" = "--no-installer" ] || [ "$1" = "no-installer" ]; then
BUILD_INSTALLER=0
echo "Skipping native installer packaging."
fi

install_apt_packages() {
if ! command -v apt-get >/dev/null 2>&1; then
echo "Automatic dependency setup supports apt-based distributions only."
return 1
fi
if [ "$(id -u)" -eq 0 ]; then
apt-get update && apt-get install -y "$@"
else
sudo apt-get update && sudo apt-get install -y "$@"
fi
}

# Java required every build. Linux packaging deps (apt) only on Linux hosts.
MISSING_TOOLS=
BUILD_JAVA_MISSING=0
APT_PACKAGES="openjdk-17-jdk"
case "$(uname -s 2>/dev/null || echo unknown)" in
Linux*) IS_LINUX=1 ;;
*) IS_LINUX=0 ;;
esac
if ! command -v java >/dev/null 2>&1; then
MISSING_TOOLS=" java"
BUILD_JAVA_MISSING=1
fi
if [ "$BUILD_INSTALLER" = "1" ] && [ "$IS_LINUX" = "1" ]; then
APT_PACKAGES="$APT_PACKAGES curl file fakeroot flatpak flatpak-builder"
for tool in jpackage curl file fakeroot flatpak flatpak-builder; do
if ! command -v "$tool" >/dev/null 2>&1; then
MISSING_TOOLS="$MISSING_TOOLS $tool"
fi
done
fi

if [ -n "$MISSING_TOOLS" ]; then
if [ "$IS_LINUX" = "1" ]; then
printf 'Missing build/package tools:%s. Install now? [y/N] ' "$MISSING_TOOLS"
read -r INSTALL_DEPS
if [ "$INSTALL_DEPS" = "y" ] || [ "$INSTALL_DEPS" = "Y" ]; then
install_apt_packages $APT_PACKAGES || exit $?
elif [ "$BUILD_JAVA_MISSING" = "1" ]; then
echo "Cannot run Gradle build without Java."
exit 1
else
BUILD_INSTALLER=0
echo "Skipping native installer packaging because dependency installation was declined."
fi
elif [ "$BUILD_JAVA_MISSING" = "1" ]; then
echo "Cannot run Gradle build without Java. Install a JDK 17+ and retry."
exit 1
fi
fi

./gradlew clean build
read -p "Press any key to continue..."
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
read -p "Press any key to continue..."
exit $EXIT_CODE
fi

if [ "$BUILD_INSTALLER" = "1" ]; then
echo "Packaging native installer..."
./gradlew package
EXIT_CODE=$?
fi

read -p "Press any key to continue..."
exit $EXIT_CODE
18 changes: 5 additions & 13 deletions creator-tools/build.gradle
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
plugins {
id 'application'
id "com.github.johnrengelman.shadow"
id 'com.gradleup.shadow'
id 'io.freefair.lombok'
}

version = "2.1.0-SNAPSHOT"

application {
mainClassName = "com.skcraft.launcher.creator.Creator"
mainClass = "com.skcraft.launcher.creator.Creator"
}

dependencies {
Expand All @@ -16,18 +16,10 @@ dependencies {
implementation 'com.jidesoft:jide-oss:3.6.18'
}


processResources {
def projectVersion = project.version.toString()
filesMatching('**/*.properties') {
filter {
it.replace('${project.version}', project.version)
}
filter { it.replace('${project.version}', projectVersion) }
}
}

shadowJar {
archiveClassifier.set("")
}

build {
dependsOn(shadowJar)
}
Loading