├── .github ├── dependabot.yml └── workflows │ └── build.yml ├── .gitignore ├── .idea ├── .gitignore ├── LiquidLauncher.iml ├── discord.xml ├── inspectionProfiles │ └── Project_Default.xml ├── modules.xml └── vcs.xml ├── .vscode └── extensions.json ├── LICENSE ├── README.md ├── gh_assets ├── screenshot-1.png ├── screenshot-2.png ├── screenshot-3.png ├── screenshot-4.png └── screenshot-5.png ├── icon.png ├── index.html ├── jsconfig.json ├── package.json ├── public └── img │ ├── banner.png │ ├── icon │ ├── icon-button-close.svg │ ├── icon-cog.svg │ ├── icon-copy-to-clipboard.svg │ ├── icon-directory-choose.svg │ ├── icon-directory-open.svg │ ├── icon-eye.svg │ ├── icon-file-choose.svg │ ├── icon-lock.svg │ ├── icon-news-scroll.svg │ ├── icon-next.svg │ ├── icon-person.svg │ ├── icon-plus.svg │ ├── icon-prev.svg │ ├── icon-version-lb.png │ ├── icon-version-mc.png │ └── social │ │ ├── icon-discord.svg │ │ ├── icon-github.svg │ │ ├── icon-nodebb.svg │ │ ├── icon-twitter.svg │ │ ├── icon-url.svg │ │ └── icon-youtube.svg │ ├── logo.svg │ └── steve.png ├── src-tauri ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── build.rs ├── capabilities │ ├── download_view.json │ └── main.json ├── icons │ ├── 128x128.png │ ├── 128x128@2x.png │ ├── 32x32.png │ ├── Square107x107Logo.png │ ├── Square142x142Logo.png │ ├── Square150x150Logo.png │ ├── Square284x284Logo.png │ ├── Square30x30Logo.png │ ├── Square310x310Logo.png │ ├── Square44x44Logo.png │ ├── Square71x71Logo.png │ ├── Square89x89Logo.png │ ├── StoreLogo.png │ ├── icon.icns │ ├── icon.ico │ └── icon.png ├── src │ ├── app │ │ ├── client_api.rs │ │ ├── gui │ │ │ ├── commands │ │ │ │ ├── auth.rs │ │ │ │ ├── client.rs │ │ │ │ ├── data.rs │ │ │ │ ├── mod.rs │ │ │ │ └── system.rs │ │ │ └── mod.rs │ │ ├── mod.rs │ │ ├── options.rs │ │ └── webview.rs │ ├── auth │ │ └── mod.rs │ ├── error.rs │ ├── main.rs │ ├── minecraft │ │ ├── auth.rs │ │ ├── java │ │ │ ├── distribution.rs │ │ │ ├── jre_downloader.rs │ │ │ ├── mod.rs │ │ │ └── runtime.rs │ │ ├── launcher │ │ │ ├── assets.rs │ │ │ ├── client_jar.rs │ │ │ ├── jre.rs │ │ │ ├── libraries.rs │ │ │ └── mod.rs │ │ ├── mod.rs │ │ ├── prelauncher.rs │ │ ├── progress.rs │ │ ├── rule_interpreter.rs │ │ └── version.rs │ └── utils │ │ ├── checksum.rs │ │ ├── download.rs │ │ ├── extract.rs │ │ ├── hosts.rs │ │ ├── macros.rs │ │ ├── maven.rs │ │ ├── mod.rs │ │ └── sys.rs ├── static │ └── success.html └── tauri.conf.json ├── src ├── App.svelte ├── app.css ├── font │ ├── Inter-Bold.ttf │ ├── Inter-Medium.ttf │ ├── Inter-Regular.ttf │ └── OFL.txt ├── lib │ ├── Window.svelte │ ├── common │ │ ├── ButtonClose.svelte │ │ ├── ButtonCopyClipboard.svelte │ │ ├── Logo.svelte │ │ ├── TitleBar.svelte │ │ ├── ToolTip.svelte │ │ ├── VerticalFlexWrapper.svelte │ │ └── social │ │ │ ├── ButtonIcon.svelte │ │ │ ├── ButtonIconText.svelte │ │ │ └── SocialBar.svelte │ ├── login │ │ ├── Facts.svelte │ │ ├── LoginScreen.svelte │ │ └── loginmodal │ │ │ ├── LoginModal.svelte │ │ │ ├── ModalButton.svelte │ │ │ └── ModalInput.svelte │ ├── main │ │ ├── Account.svelte │ │ ├── ButtonLaunchArea.svelte │ │ ├── ButtonVersion.svelte │ │ ├── ContentWrapper.svelte │ │ ├── ErrorScreen.svelte │ │ ├── FirstRunWarning.svelte │ │ ├── LaunchArea.svelte │ │ ├── LoadingScreen.svelte │ │ ├── MainHeader.svelte │ │ ├── MainScreen.svelte │ │ ├── NonSecureConnectionScreen.svelte │ │ ├── VersionSelect.svelte │ │ ├── VersionWarning.svelte │ │ ├── log │ │ │ ├── ClientLog.svelte │ │ │ ├── LogMessage.svelte │ │ │ └── VirtualList.svelte │ │ ├── news │ │ │ ├── News.svelte │ │ │ └── NewsArea.svelte │ │ ├── settings │ │ │ ├── GeneralSettings.svelte │ │ │ ├── PremiumSettings.svelte │ │ │ └── Settings.svelte │ │ └── statusbar │ │ │ ├── ProgressStatus.svelte │ │ │ ├── StatusBar.svelte │ │ │ └── TextStatus.svelte │ └── settings │ │ ├── ButtonSetting.svelte │ │ ├── CustomModSetting.svelte │ │ ├── Description.svelte │ │ ├── DirectorySelectorSetting.svelte │ │ ├── FileSelectorSetting.svelte │ │ ├── IconButtonSetting.svelte │ │ ├── LauncherVersion.svelte │ │ ├── LiquidBounceAccount.svelte │ │ ├── RangeSetting.svelte │ │ ├── RangeSettingStyles.css │ │ ├── SelectSetting.svelte │ │ ├── SettingWrapper.svelte │ │ ├── SettingsContainer.svelte │ │ ├── TextSetting.svelte │ │ ├── ToggleSetting.svelte │ │ └── tab │ │ ├── Tab.svelte │ │ └── Tabs.svelte ├── main.js └── vite-env.d.ts ├── vite.config.js └── yarn.lock /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "src-tauri/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | push: 4 | branches: 5 | - tauri/v2 6 | 7 | jobs: 8 | build-tauri: 9 | permissions: 10 | contents: write 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | include: 15 | - platform: macos-latest 16 | args: >- 17 | --target aarch64-apple-darwin 18 | - platform: macos-latest 19 | args: >- 20 | --target x86_64-apple-darwin 21 | - platform: ubuntu-22.04 22 | args: >- 23 | 24 | - platform: windows-latest 25 | args: >- 26 | 27 | 28 | runs-on: ${{ matrix.platform }} 29 | steps: 30 | - name: Checkout repository and submodules 31 | uses: actions/checkout@v4 32 | with: 33 | submodules: recursive 34 | 35 | - name: setup node 36 | uses: actions/setup-node@v4 37 | with: 38 | node-version: 21 39 | 40 | - name: install Rust nightly 41 | uses: dtolnay/rust-toolchain@nightly 42 | with: 43 | targets: >- 44 | ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }} 45 | 46 | - name: install dependencies (ubuntu only) 47 | if: matrix.platform == 'ubuntu-22.04' 48 | run: | 49 | sudo apt-get update 50 | sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libx11-dev xorg-dev libglu1-mesa-dev libglib2.0-0 51 | 52 | - name: Setup Cargo dependency cache 53 | uses: Swatinem/rust-cache@v2 54 | with: 55 | shared-key: shared 56 | 57 | - name: install app dependencies and build it 58 | run: | 59 | yarn && yarn build 60 | 61 | - name: Build and release 62 | uses: tauri-apps/tauri-action@v0 63 | env: 64 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 65 | TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} 66 | TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} 67 | with: 68 | args: >- 69 | ${{ matrix.args }} 70 | tagName: >- 71 | ${{ startsWith(github.event.head_commit.message, 'release: ') && 'v__VERSION__' || format('v__VERSION__-{0}', github.sha) }} 72 | releaseName: >- 73 | ${{ startsWith(github.event.head_commit.message, 'release: ') && 'Release v__VERSION__' || format('Development build - {0}', github.sha) }} 74 | releaseBody: >- 75 | ${{ startsWith(github.event.head_commit.message, 'release: ') && 'See the assets to download and install this version.' || format('Development build from commit {0} 76 | 77 | This is an automated build from the latest commit. It may be unstable. 78 | 79 | Commit message: {1}', github.sha, github.event.head_commit.message) }} 80 | releaseDraft: >- 81 | ${{ startsWith(github.event.head_commit.message, 'release: ') }} 82 | prerelease: >- 83 | ${{ !startsWith(github.event.head_commit.message, 'release: ') }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /public/build/ 3 | /dist 4 | /tauri 5 | 6 | .DS_Store 7 | .env -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/LiquidLauncher.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/discord.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 43 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"] 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LiquidLauncher 2 | The official launcher for LiquidBounce. 3 | 4 | Website: https://liquidbounce.net \ 5 | Forum: https://forums.ccbluex.net \ 6 | Guilded: https://guilded.gg/CCBlueX \ 7 | YouTube: https://youtube.com/CCBlueX \ 8 | Twitter: https://twitter.com/CCBlueX 9 | 10 | ## Screenshots 11 | 12 | 13 | 16 | 19 | 20 | 21 | 24 | 27 | 28 | 29 | 32 | 33 |
14 | 15 | 17 | 18 |
22 | 23 | 25 | 26 |
30 | 31 |
34 | 35 | ## Issues 36 | If you notice any bugs or missing features, you can let us know by opening an issue [here](https://github.com/CCBlueX/LiquidLauncher/issues). 37 | 38 | ## License 39 | This project is subject to the [GNU General Public License v3.0](LICENSE). This does only apply for source code located directly in this clean repository. During the development and compilation process, additional source code may be used to which we have obtained no rights. Such code is not covered by the GPL license. 40 | 41 | For those who are unfamiliar with the license, here is a summary of its main points. This is by no means legal advice nor legally binding. 42 | 43 | You are allowed to 44 | - use 45 | - share 46 | - modify 47 | 48 | this project entirely or partially for free and even commercially. However, please consider the following: 49 | 50 | - **You must disclose the source code of your modified work and the source code you took from this project. This means you are not allowed to use code from this project (even partially) in a closed-source (or even obfuscated) application.** 51 | - **Your modified application must also be licensed under the GPL** 52 | 53 | Do the above and share your source code with everyone; just like we do. 54 | 55 | ## Icons 56 | We use [Clarity Line Icons](https://www.svgrepo.com/collection/clarity-line-icons/) for this project. 57 | 58 | ## Compile it yourself! 59 | LiquidLauncher is using Tauri and is written in the programming language Rust, so make sure that it is installed properly. Instructions can be found on [Rust's website](https://www.rust-lang.org/learn/get-started). It also requires NodeJS and yarn. 60 | 1. Clone the repository using `git clone --recurse-submodules https://github.com/CCBlueX/LiquidLauncher`. 61 | 2. Navigate into your local repository folder. 62 | 3. Execute the command `yarn && yarn build` 63 | 4. Now you can start the launcher using `npm run tauri dev` or build it by using `npm run tauri build` 64 | 65 | ## Contributing 66 | 67 | We appreciate contributions. So if you want to support us, feel free to make changes to LiquidLauncher's source code and submit a pull request. 68 | -------------------------------------------------------------------------------- /gh_assets/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/gh_assets/screenshot-1.png -------------------------------------------------------------------------------- /gh_assets/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/gh_assets/screenshot-2.png -------------------------------------------------------------------------------- /gh_assets/screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/gh_assets/screenshot-3.png -------------------------------------------------------------------------------- /gh_assets/screenshot-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/gh_assets/screenshot-4.png -------------------------------------------------------------------------------- /gh_assets/screenshot-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/gh_assets/screenshot-5.png -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/icon.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LiquidLauncher 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "Node", 4 | "target": "ESNext", 5 | "module": "ESNext", 6 | /** 7 | * svelte-preprocess cannot figure out whether you have 8 | * a value or a type, so tell TypeScript to enforce using 9 | * `import type` instead of `import` for Types. 10 | */ 11 | "importsNotUsedAsValues": "error", 12 | "isolatedModules": true, 13 | "resolveJsonModule": true, 14 | /** 15 | * To have warnings / errors of the Svelte compiler at the 16 | * correct position, enable source maps by default. 17 | */ 18 | "sourceMap": true, 19 | "esModuleInterop": true, 20 | "skipLibCheck": true, 21 | "forceConsistentCasingInFileNames": true, 22 | /** 23 | * Typecheck JS in `.svelte` and `.js` files by default. 24 | * Disable this if you'd like to use dynamic types. 25 | */ 26 | "checkJs": true 27 | }, 28 | /** 29 | * Use global.d.ts instead of compilerOptions.types 30 | * to avoid limiting type declarations. 31 | */ 32 | "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] 33 | } 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "liquidlauncher", 3 | "private": true, 4 | "version": "0.5.0", 5 | "type": "module", 6 | "license": "GPL-3.0", 7 | "scripts": { 8 | "dev": "vite", 9 | "build": "vite build", 10 | "preview": "vite preview", 11 | "tauri": "tauri" 12 | }, 13 | "devDependencies": { 14 | "@sveltejs/vite-plugin-svelte": "^3.0", 15 | "@tauri-apps/cli": "^2", 16 | "svelte": "^4.2", 17 | "svelte-loading-spinners": "^0.3.6", 18 | "vite": "^5.0" 19 | }, 20 | "dependencies": { 21 | "@tauri-apps/api": "^2", 22 | "@tauri-apps/plugin-clipboard-manager": "^2", 23 | "@tauri-apps/plugin-dialog": "^2", 24 | "@tauri-apps/plugin-opener": "^2", 25 | "@tauri-apps/plugin-process": "^2", 26 | "@tauri-apps/plugin-updater": "^2", 27 | "nouislider": "^15.7" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /public/img/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/public/img/banner.png -------------------------------------------------------------------------------- /public/img/icon/icon-button-close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/img/icon/icon-copy-to-clipboard.svg: -------------------------------------------------------------------------------- 1 | 2 | copy-to-clipboard-line 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/img/icon/icon-directory-choose.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | directory-line 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/img/icon/icon-directory-open.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | directory-outline-alerted 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/img/icon/icon-eye.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/img/icon/icon-file-choose.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | file-line 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/img/icon/icon-lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/img/icon/icon-news-scroll.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/img/icon/icon-next.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/img/icon/icon-person.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/img/icon/icon-plus.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/icon/icon-prev.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/img/icon/icon-version-lb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/public/img/icon/icon-version-lb.png -------------------------------------------------------------------------------- /public/img/icon/icon-version-mc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/public/img/icon/icon-version-mc.png -------------------------------------------------------------------------------- /public/img/icon/social/icon-discord.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/img/icon/social/icon-github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/img/icon/social/icon-nodebb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/img/icon/social/icon-twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/img/icon/social/icon-url.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /public/img/icon/social/icon-youtube.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/img/logo.svg: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /public/img/steve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/public/img/steve.png -------------------------------------------------------------------------------- /src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | /gen/ -------------------------------------------------------------------------------- /src-tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "liquidlauncher" 3 | version = "0.5.0" 4 | description = "A LiquidBounce launcher for Minecraft, written in Rust using Tauri." 5 | authors = ["Izuna ", "superblaubeere27"] 6 | license = "GNU General Public License v3.0" 7 | homepage = "https://liquidbounce.net/" 8 | repository = "https://github.com/CCBlueX/LiquidLauncher" 9 | edition = "2021" 10 | rust-version = "1.79.0" 11 | 12 | [build-dependencies] 13 | tauri-build = { version = "^2", features = [] } 14 | 15 | [dependencies] 16 | # Log 17 | tracing = "0.1" 18 | tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] } 19 | tracing-appender = "0.2" 20 | 21 | # Async IO 22 | tokio = { version = "1", features = ["full"] } 23 | futures = "0.3" 24 | 25 | # Error handling 26 | anyhow = "1.0" 27 | thiserror = "2.0" 28 | backon = { version = "1.3.0", default-features = false, features = ["tokio-sleep"] } 29 | 30 | # Generic 31 | void = "1" 32 | directories = "5.0" 33 | once_cell = "1.16.0" 34 | 35 | # FS libs 36 | async_zip = { version = "0.0.11", features = ["full"] } 37 | tokio-tar = "0.3.0" 38 | async-compression = { version= "0.3.15", features = ["gzip"] } 39 | sanitize-filename = "0.6.0" 40 | path-absolutize = "3.0" 41 | 42 | # OS 43 | os_info = "3" 44 | sysinfo = "0.29" 45 | 46 | # Data 47 | regex = "1.7.0" 48 | uuid = { version = "1.2", features = ["serde", "v4"] } 49 | chrono = { version = "0.4", features = ["serde"] } 50 | rand = "0.9" 51 | 52 | sha1 = "0.10" 53 | base16ct = {version = "0.2.0", features = ["alloc"] } 54 | 55 | # UI library 56 | tauri = { version = "^2", features = [] } 57 | tauri-plugin-process = "^2" 58 | tauri-plugin-dialog = "^2" 59 | tauri-plugin-opener = "^2" 60 | tauri-plugin-updater = "^2" 61 | tauri-plugin-clipboard-manager = "^2" 62 | 63 | # HTTP library 64 | reqwest = { version = "0.12", default-features = false, features = ["http2", "rustls-tls", "charset", "gzip", "brotli", "zstd", "deflate", "json"] } 65 | 66 | # Framework for serializing and deserializing data 67 | serde = { version = "1.0", features = ["derive"] } 68 | serde_json = "1.0" 69 | 70 | # MS Authentication support 71 | azalea-auth = { git = "https://github.com/CCBlueX/azalea.git", branch = "custom_auth", package = "azalea-auth" } 72 | 73 | md5 = "0.7.0" 74 | oauth2 = "5.0.0-alpha.4" 75 | 76 | [features] 77 | # by default Tauri runs in production mode 78 | # when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL 79 | default = [ "custom-protocol" ] 80 | # this feature is used used for production builds where `devPath` points to the filesystem 81 | # DO NOT remove this 82 | custom-protocol = [ "tauri/custom-protocol" ] 83 | 84 | [profile.release] 85 | strip = true 86 | lto = true 87 | codegen-units = 1 88 | -------------------------------------------------------------------------------- /src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | 20 | fn main() { 21 | tauri_build::build() 22 | } 23 | -------------------------------------------------------------------------------- /src-tauri/capabilities/download_view.json: -------------------------------------------------------------------------------- 1 | { 2 | "identifier": "download-capability", 3 | "description": "Capability for the download view", 4 | "remote": { 5 | "urls": ["https://dl.liquidbounce.net", "https://dl.ccbluex.net"] 6 | }, 7 | "windows": ["download_view-*"], 8 | "local": false, 9 | "permissions": [ 10 | "core:event:default" 11 | ] 12 | } -------------------------------------------------------------------------------- /src-tauri/capabilities/main.json: -------------------------------------------------------------------------------- 1 | { 2 | "identifier": "main-capability", 3 | "description": "Capability for the main window", 4 | "windows": [ 5 | "main" 6 | ], 7 | "permissions": [ 8 | "core:path:default", 9 | "core:event:default", 10 | "core:window:default", 11 | "core:app:default", 12 | "core:resources:default", 13 | "core:menu:default", 14 | "core:tray:default", 15 | "core:window:allow-set-title", 16 | "core:window:allow-start-dragging", 17 | "dialog:allow-message", 18 | "dialog:allow-open", 19 | "dialog:allow-confirm", 20 | "dialog:allow-ask", 21 | "clipboard-manager:allow-write-text", 22 | "opener:default", 23 | "process:allow-exit", 24 | "updater:default" 25 | ] 26 | } -------------------------------------------------------------------------------- /src-tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src-tauri/icons/128x128.png -------------------------------------------------------------------------------- /src-tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src-tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src-tauri/icons/32x32.png -------------------------------------------------------------------------------- /src-tauri/icons/Square107x107Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src-tauri/icons/Square107x107Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square142x142Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src-tauri/icons/Square142x142Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src-tauri/icons/Square150x150Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square284x284Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src-tauri/icons/Square284x284Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square30x30Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src-tauri/icons/Square30x30Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square310x310Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src-tauri/icons/Square310x310Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src-tauri/icons/Square44x44Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square71x71Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src-tauri/icons/Square71x71Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square89x89Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src-tauri/icons/Square89x89Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src-tauri/icons/StoreLogo.png -------------------------------------------------------------------------------- /src-tauri/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src-tauri/icons/icon.icns -------------------------------------------------------------------------------- /src-tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src-tauri/icons/icon.ico -------------------------------------------------------------------------------- /src-tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src-tauri/icons/icon.png -------------------------------------------------------------------------------- /src-tauri/src/app/gui/commands/auth.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | 20 | use tauri::{Emitter, Window}; 21 | use tracing::{debug, info}; 22 | 23 | use crate::app::client_api::Client; 24 | use crate::{ 25 | auth::{ClientAccount, ClientAccountAuthenticator}, 26 | minecraft::auth::MinecraftAccount, 27 | }; 28 | 29 | #[tauri::command] 30 | pub(crate) async fn login_offline(username: &str) -> Result { 31 | let account = MinecraftAccount::auth_offline(username.to_string()).await; 32 | Ok(account) 33 | } 34 | 35 | #[tauri::command] 36 | pub(crate) async fn login_microsoft(window: Window) -> Result { 37 | let account = MinecraftAccount::auth_msa(|uri, code| { 38 | debug!("enter code {} on {} to sign-in", code, uri); 39 | let _ = window.emit("microsoft_code", code); 40 | }) 41 | .await 42 | .map_err(|e| format!("{}", e))?; 43 | 44 | Ok(account) 45 | } 46 | 47 | #[tauri::command] 48 | pub(crate) async fn client_account_authenticate(client: Client) -> Result { 49 | let mut account = ClientAccountAuthenticator::start_auth(|uri| { 50 | let _ = tauri_plugin_opener::open_url(uri, None::<&str>); 51 | }) 52 | .await 53 | .map_err(|e| format!("{}", e))?; 54 | 55 | account 56 | .update_info(&client) 57 | .await 58 | .map_err(|e| format!("unable to fetch user information: {:?}", e))?; 59 | 60 | Ok(account) 61 | } 62 | 63 | #[tauri::command] 64 | pub(crate) async fn client_account_update(client: Client, account: ClientAccount) -> Result { 65 | let mut account = account 66 | .renew() 67 | .await 68 | .map_err(|e| format!("unable to update access token: {:?}", e))?; 69 | 70 | account 71 | .update_info(&client) 72 | .await 73 | .map_err(|e| format!("unable to fetch user information: {:?}", e))?; 74 | Ok(account) 75 | } 76 | 77 | #[tauri::command] 78 | pub(crate) async fn refresh(account_data: MinecraftAccount) -> Result { 79 | info!("Refreshing account..."); 80 | let account = account_data 81 | .refresh() 82 | .await 83 | .map_err(|e| format!("unable to refresh: {:?}", e))?; 84 | info!( 85 | "Account was refreshed - username {}", 86 | account.get_username() 87 | ); 88 | Ok(account) 89 | } 90 | 91 | #[tauri::command] 92 | pub(crate) async fn logout(account_data: MinecraftAccount) -> Result<(), String> { 93 | account_data 94 | .logout() 95 | .await 96 | .map_err(|e| format!("unable to logout: {:?}", e)) 97 | } -------------------------------------------------------------------------------- /src-tauri/src/app/gui/commands/data.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | 20 | use crate::{ 21 | app::options::Options, 22 | LAUNCHER_DIRECTORY 23 | }; 24 | 25 | #[tauri::command] 26 | pub(crate) async fn get_options() -> Result { 27 | let config_dir = LAUNCHER_DIRECTORY.config_dir(); 28 | let options = Options::load(config_dir).await.unwrap_or_default(); 29 | Ok(options) 30 | } 31 | 32 | #[tauri::command] 33 | pub(crate) async fn store_options(options: Options) -> Result<(), String> { 34 | let config_dir = LAUNCHER_DIRECTORY.config_dir(); 35 | options 36 | .store(config_dir) 37 | .await 38 | .map_err(|e| format!("unable to store config data: {:?}", e))?; 39 | Ok(()) 40 | } 41 | 42 | #[tauri::command] 43 | pub(crate) async fn clear_data(options: Options) -> Result<(), String> { 44 | let data_directory = if !options.start_options.custom_data_path.is_empty() { 45 | Some(options.start_options.custom_data_path) 46 | } else { 47 | None 48 | } 49 | .map(|x| x.into()) 50 | .unwrap_or_else(|| LAUNCHER_DIRECTORY.data_dir().to_path_buf()); 51 | 52 | [ 53 | "assets", 54 | "gameDir", 55 | "libraries", 56 | "mod_cache", 57 | "natives", 58 | "runtimes", 59 | "versions", 60 | ] 61 | .iter() 62 | .map(|dir| data_directory.join(dir)) 63 | .filter(|dir| dir.exists()) 64 | .map(std::fs::remove_dir_all) 65 | .collect::, _>>() 66 | .map_err(|e| format!("unable to clear data: {:?}", e))?; 67 | Ok(()) 68 | } 69 | 70 | #[tauri::command] 71 | pub(crate) async fn default_data_folder_path() -> Result { 72 | let data_directory = LAUNCHER_DIRECTORY.data_dir().to_str(); 73 | 74 | match data_directory { 75 | None => Err("unable to get data folder path".to_string()), 76 | Some(path) => Ok(path.to_string()), 77 | } 78 | } -------------------------------------------------------------------------------- /src-tauri/src/app/gui/commands/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | 20 | pub(crate) mod auth; 21 | pub(crate) mod client; 22 | pub(crate) mod data; 23 | pub(crate) mod system; 24 | 25 | pub(crate) use auth::*; 26 | pub(crate) use client::*; 27 | pub(crate) use data::*; 28 | pub(crate) use system::*; 29 | -------------------------------------------------------------------------------- /src-tauri/src/app/gui/commands/system.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | use crate::app::client_api::Client; 20 | use crate::{utils, LAUNCHER_VERSION}; 21 | use tracing::{debug, debug_span, info}; 22 | 23 | #[tauri::command] 24 | pub(crate) async fn get_launcher_version() -> Result { 25 | Ok(LAUNCHER_VERSION.to_string()) 26 | } 27 | 28 | #[tauri::command] 29 | pub(crate) async fn setup_client(session_token: String) -> Result { 30 | Client::lookup(session_token) 31 | .await 32 | } 33 | 34 | #[tauri::command] 35 | pub(crate) async fn check_system() -> Result<(), String> { 36 | let span = debug_span!("system_check"); 37 | let _guard = span.enter(); 38 | 39 | debug!(parent: &span, "Checking system..."); 40 | 41 | #[cfg(windows)] 42 | { 43 | use crate::utils::check_hosts_file; 44 | check_hosts_file().await.map_err(|e| format!("{}", e))?; 45 | } 46 | 47 | info!(parent: &span, "Successfully checked system."); 48 | Ok(()) 49 | } 50 | 51 | #[tauri::command] 52 | pub(crate) fn sys_memory() -> u64 { 53 | utils::sys_memory() / (1024 * 1024) 54 | } 55 | -------------------------------------------------------------------------------- /src-tauri/src/app/gui/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | 20 | use std::sync::{Arc, Mutex}; 21 | 22 | use commands::*; 23 | use tauri::Window; 24 | 25 | pub type ShareableWindow = Arc>; 26 | 27 | pub struct RunnerInstance { 28 | pub terminator: tokio::sync::oneshot::Sender<()>, 29 | } 30 | 31 | pub struct AppState { 32 | pub runner_instance: Arc>>, 33 | } 34 | 35 | impl AppState { 36 | pub fn new() -> Self { 37 | Self { 38 | runner_instance: Arc::new(Mutex::new(None)), 39 | } 40 | } 41 | } 42 | 43 | mod commands; 44 | 45 | /// Runs the GUI and returns when the window is closed. 46 | pub fn gui_main() { 47 | tauri::Builder::default() 48 | .plugin(tauri_plugin_updater::Builder::new().build()) 49 | .plugin(tauri_plugin_process::init()) 50 | .plugin(tauri_plugin_opener::init()) 51 | .plugin(tauri_plugin_dialog::init()) 52 | .plugin(tauri_plugin_clipboard_manager::init()) 53 | .manage(AppState::new()) 54 | .invoke_handler(tauri::generate_handler![ 55 | setup_client, 56 | check_system, 57 | sys_memory, 58 | get_options, 59 | store_options, 60 | request_branches, 61 | request_builds, 62 | request_mods, 63 | run_client, 64 | login_offline, 65 | login_microsoft, 66 | client_account_authenticate, 67 | client_account_update, 68 | logout, 69 | refresh, 70 | fetch_blog_posts, 71 | fetch_changelog, 72 | clear_data, 73 | default_data_folder_path, 74 | terminate, 75 | get_launcher_version, 76 | get_custom_mods, 77 | install_custom_mod, 78 | delete_custom_mod 79 | ]) 80 | .run(tauri::generate_context!()) 81 | .expect("error while running tauri application"); 82 | } 83 | -------------------------------------------------------------------------------- /src-tauri/src/app/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | 20 | pub mod gui; 21 | 22 | pub mod options; 23 | pub mod webview; 24 | pub mod client_api; 25 | -------------------------------------------------------------------------------- /src-tauri/src/app/webview.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | 20 | use crate::minecraft::{ 21 | launcher::LauncherData, 22 | progress::{ProgressReceiver, ProgressUpdate}, 23 | }; 24 | use anyhow::{anyhow, bail, Context, Result}; 25 | use serde::Deserialize; 26 | use std::{ 27 | sync::{ 28 | atomic::{AtomicBool, Ordering}, 29 | Arc, Mutex, 30 | }, 31 | time::Duration, 32 | }; 33 | use tauri::{Listener, Manager, Url, WebviewUrl, WebviewWindowBuilder, WindowEvent}; 34 | use tokio::time::sleep; 35 | use tracing::{debug, info}; 36 | 37 | use super::gui::ShareableWindow; 38 | 39 | const MAX_DOWNLOAD_ATTEMPTS: u8 = 2; 40 | 41 | pub async fn open_download_page( 42 | url: &str, 43 | launcher_data: &LauncherData, 44 | ) -> Result { 45 | let download_page: Url = url.parse() 46 | .context("Failed to parse download page URL")?; 47 | 48 | let mut count = 0; 49 | 50 | let url = loop { 51 | count += 1; 52 | 53 | if count > MAX_DOWNLOAD_ATTEMPTS { 54 | bail!("Failed to open download page after {} attempts.\n\n\ 55 | Please do not close the download window. Instead proceed with the download by pressing on 'Continue' and then 'Download'.\n\n\ 56 | If the download window does not appear, please try restarting LiquidLauncher with administrator privileges.\n\ 57 | If this does not help, please install LiquidBounce manually (https://liquidbounce.net/docs/get-started/manual-installation).\n\ 58 | Or try our advice at https://liquidbounce.net/docs/tutorials/fixing-liquidlauncher.", MAX_DOWNLOAD_ATTEMPTS); 59 | } 60 | 61 | launcher_data.progress_update(ProgressUpdate::SetLabel(format!( 62 | "Opening download page... (Attempt {}/{})", 63 | count, MAX_DOWNLOAD_ATTEMPTS 64 | ))); 65 | 66 | match show_webview(download_page.clone(), &launcher_data.data).await { 67 | Ok(pid) => break pid, 68 | Err(e) => { 69 | launcher_data.log(&format!("Failed to open download page: {:?}", e)); 70 | sleep(Duration::from_millis(500)).await; 71 | } 72 | } 73 | }; 74 | 75 | Ok(url) 76 | } 77 | 78 | async fn show_webview(url: Url, window: &Arc>) -> Result { 79 | let window = window 80 | .lock() 81 | .map_err(|_| anyhow!("Failed to lock window"))?; 82 | let app = window.app_handle(); 83 | let main_window = window.get_webview_window("main") 84 | .ok_or_else(|| anyhow!("Failed to get window"))?; 85 | let len = app.webview_windows().len(); 86 | 87 | let download_view = WebviewWindowBuilder::new(app, format!("download_view-{}", len), WebviewUrl::External(url)) 88 | .title("Download of LiquidBounce JAR") 89 | .visible(true) 90 | .always_on_top(true) 91 | .maximized(true) 92 | .center() 93 | .parent(&main_window)? 94 | .build()?; 95 | drop(window); 96 | 97 | // Wait for the download to finish 98 | let pid_cell = Arc::new(Mutex::new(None)); 99 | let close_request = Arc::new(AtomicBool::new(false)); 100 | let cloned_close_request = close_request.clone(); 101 | let cloned_cell = pid_cell.clone(); 102 | 103 | download_view.on_window_event(move |event| { 104 | if let WindowEvent::CloseRequested { api, .. } = event { 105 | api.prevent_close(); 106 | close_request.store(true, Ordering::SeqCst); 107 | } 108 | }); 109 | 110 | download_view.once("download", move |event| { 111 | debug!("Download Event received: {:?}", event); 112 | let payload = event.payload(); 113 | 114 | #[derive(Deserialize)] 115 | struct DownloadPayload { 116 | pid: String 117 | } 118 | 119 | let payload = serde_json::from_str::(payload).unwrap(); 120 | 121 | info!("Received PID: {}", payload.pid); 122 | *cloned_cell.lock().unwrap() = Some(payload.pid); 123 | }); 124 | 125 | let pid = loop { 126 | // sleep for 100ms 127 | sleep(Duration::from_millis(100)).await; 128 | 129 | // check if we got the download link 130 | if let Ok(pid) = pid_cell.lock() { 131 | if let Some(pid) = pid.clone() { 132 | break pid; 133 | } 134 | } 135 | 136 | if cloned_close_request.load(Ordering::SeqCst) { 137 | let _ = download_view.hide(); 138 | bail!( 139 | "Download view was closed before the file PID was received. \ 140 | Aborting download..." 141 | ); 142 | } 143 | 144 | download_view 145 | .is_visible() 146 | .context("Download view was closed unexpected")?; 147 | }; 148 | 149 | let _ = download_view.destroy(); 150 | 151 | Ok(pid) 152 | } 153 | -------------------------------------------------------------------------------- /src-tauri/src/error.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | use anyhow::{anyhow, Error}; 20 | use thiserror::Error; 21 | 22 | #[derive(Error, Debug)] 23 | pub enum LauncherError { 24 | #[error("Invalid version profile: {0}")] 25 | InvalidVersionProfile(String), 26 | #[error("Unknown template parameter: {0}")] 27 | UnknownTemplateParameter(String), 28 | } 29 | 30 | pub fn map_into_connection_error(e: Error) -> Error { 31 | anyhow!( 32 | "Failed to download file. This might have been caused by connection issues. Please try using a VPN such as Cloudflare Warp.\n\nError: {}", 33 | e 34 | ) 35 | } -------------------------------------------------------------------------------- /src-tauri/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | #![feature(duration_constructors)] 20 | #![cfg_attr( 21 | all(not(debug_assertions), target_os = "windows"), 22 | windows_subsystem = "windows" 23 | )] 24 | 25 | use crate::app::gui::gui_main; 26 | use crate::utils::{OS, OS_VERSION}; 27 | use anyhow::Result; 28 | use directories::ProjectDirs; 29 | use once_cell::sync::Lazy; 30 | use reqwest::Client; 31 | use std::io; 32 | use tracing::{debug, debug_span, error, info}; 33 | use tracing_subscriber::layer::SubscriberExt; 34 | use utils::ARCHITECTURE; 35 | 36 | pub mod app; 37 | pub mod auth; 38 | pub mod minecraft; 39 | 40 | mod error; 41 | mod utils; 42 | 43 | const LAUNCHER_VERSION: &str = env!("CARGO_PKG_VERSION"); 44 | static LAUNCHER_DIRECTORY: Lazy = 45 | Lazy::new( 46 | || match ProjectDirs::from("net", "CCBlueX", "LiquidLauncher") { 47 | Some(proj_dirs) => proj_dirs, 48 | None => panic!("no application directory"), 49 | }, 50 | ); 51 | 52 | static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")); 53 | 54 | /// HTTP Client with launcher agent 55 | static HTTP_CLIENT: Lazy = Lazy::new(|| { 56 | let client = reqwest::ClientBuilder::new() 57 | .user_agent(APP_USER_AGENT) 58 | .build() 59 | .unwrap_or_else(|_| Client::new()); 60 | 61 | client 62 | }); 63 | 64 | pub fn main() -> Result<()> { 65 | use tracing_subscriber::{fmt, EnvFilter}; 66 | 67 | let logs = LAUNCHER_DIRECTORY.data_dir().join("logs"); 68 | if let Err(e) = utils::clean_directory(&logs, 7) { 69 | error!("Failed to clear log folder: {:?}", e); 70 | } 71 | 72 | let file_appender = tracing_appender::rolling::daily(logs, "launcher.log"); 73 | 74 | let subscriber = tracing_subscriber::registry() 75 | .with(EnvFilter::from("liquidlauncher=debug")) 76 | .with( 77 | fmt::Layer::new() 78 | .with_ansi(true) 79 | .with_writer(io::stdout), 80 | ) 81 | .with( 82 | fmt::Layer::new() 83 | .with_ansi(false) 84 | .with_writer(file_appender), 85 | ); 86 | tracing::subscriber::set_global_default(subscriber).expect("Unable to set a global subscriber"); 87 | 88 | { 89 | let span = debug_span!("startup"); 90 | let _guard = span.enter(); 91 | 92 | info!(parent: &span, "Starting LiquidLauncher v{}", LAUNCHER_VERSION); 93 | info!(parent: &span, "OS: {:} {:} {:}", OS, *ARCHITECTURE, OS_VERSION.to_string()); 94 | 95 | // application directory 96 | info!(parent: &span, "Creating application directory"); 97 | debug!(parent: &span, "Application directory: {:?}", LAUNCHER_DIRECTORY.data_dir()); 98 | debug!(parent: &span, "Config directory: {:?}", LAUNCHER_DIRECTORY.config_dir()); 99 | mkdir!(LAUNCHER_DIRECTORY.data_dir()); 100 | mkdir!(LAUNCHER_DIRECTORY.config_dir()); 101 | 102 | info!(parent: &span, "Starting GUI using Tauri framework {}", tauri::VERSION); 103 | } 104 | 105 | // Start the GUI 106 | gui_main(); 107 | 108 | info!("Launcher exited"); 109 | Ok(()) 110 | } 111 | -------------------------------------------------------------------------------- /src-tauri/src/minecraft/java/distribution.rs: -------------------------------------------------------------------------------- 1 | use anyhow::bail; 2 | use crate::utils::{ARCHITECTURE, OS}; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Deserialize, Serialize, Clone)] 6 | #[serde(tag = "type", content = "value")] 7 | pub enum DistributionSelection { 8 | #[serde(rename = "automatic")] 9 | Automatic(String), // (String) is useless, but required for deserialization 10 | #[serde(rename = "custom")] 11 | Custom(String), 12 | #[serde(rename = "manual")] 13 | Manual(JavaDistribution), 14 | } 15 | 16 | impl Default for DistributionSelection { 17 | fn default() -> Self { 18 | DistributionSelection::Automatic(String::new()) 19 | } 20 | } 21 | 22 | #[derive(Deserialize, Serialize, Clone)] 23 | pub enum JavaDistribution { 24 | #[serde(rename = "temurin")] 25 | Temurin, 26 | #[serde(rename = "graalvm")] 27 | GraalVM, 28 | } 29 | 30 | impl Default for JavaDistribution { 31 | fn default() -> Self { 32 | // Temurin supports any version of java 33 | JavaDistribution::Temurin 34 | } 35 | } 36 | 37 | impl JavaDistribution { 38 | pub fn get_url(&self, jre_version: &u32) -> anyhow::Result { 39 | let os_arch = ARCHITECTURE.get_simple_name()?; 40 | let archive_type = OS.get_archive_type()?; 41 | 42 | Ok(match self { 43 | JavaDistribution::Temurin => { 44 | let os_name = OS.get_adoptium_name()?; 45 | format!( 46 | "https://api.adoptium.net/v3/binary/latest/{}/ga/{}/{}/jre/hotspot/normal/eclipse?project=jdk", 47 | jre_version, os_name, os_arch 48 | ) 49 | } 50 | JavaDistribution::GraalVM => { 51 | let os_name = OS.get_graal_name()?; 52 | 53 | if jre_version > &17 { 54 | format!( 55 | "https://download.oracle.com/graalvm/{}/latest/graalvm-jdk-{}_{}-{}_bin.{}", 56 | jre_version, jre_version, os_name, os_arch, archive_type 57 | ) 58 | } else if jre_version == &17 { 59 | // Use archive link for 17.0.12 60 | format!( 61 | "https://download.oracle.com/graalvm/17/archive/graalvm-jdk-17.0.12_{}-{}_bin.{}", 62 | os_name, os_arch, archive_type 63 | ) 64 | } else { 65 | bail!("GraalVM only supports Java 17+") 66 | } 67 | } 68 | }) 69 | } 70 | 71 | pub fn get_name(&self) -> &str { 72 | match self { 73 | JavaDistribution::Temurin => "temurin", 74 | JavaDistribution::GraalVM => "graalvm", 75 | } 76 | } 77 | 78 | pub fn supports_version(&self, version: u32) -> bool { 79 | match self { 80 | JavaDistribution::Temurin => true, // Supports 8, 11, 17, 21 81 | JavaDistribution::GraalVM => version >= 17, // Only supports 17+ 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src-tauri/src/minecraft/java/jre_downloader.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | 20 | use std::io::Cursor; 21 | use std::path::{Path, PathBuf}; 22 | 23 | use anyhow::{bail, Result}; 24 | use path_absolutize::Absolutize; 25 | use tokio::fs; 26 | 27 | use crate::utils::{download_file, tar_gz_extract, zip_extract, OperatingSystem, OS}; 28 | 29 | use super::JavaDistribution; 30 | 31 | /// Find java binary in JRE folder 32 | pub async fn find_java_binary( 33 | runtimes_folder: &Path, 34 | jre_distribution: &JavaDistribution, 35 | jre_version: &u32, 36 | ) -> Result { 37 | let runtime_path = 38 | runtimes_folder.join(format!("{}_{}", jre_distribution.get_name(), jre_version)); 39 | 40 | // Find JRE in runtime folder 41 | let mut files = fs::read_dir(&runtime_path).await?; 42 | 43 | if let Some(jre_folder) = files.next_entry().await? { 44 | let folder_path = jre_folder.path(); 45 | 46 | let java_binary = match OS { 47 | OperatingSystem::WINDOWS => folder_path.join("bin").join("javaw.exe"), 48 | OperatingSystem::OSX => folder_path 49 | .join("Contents") 50 | .join("Home") 51 | .join("bin") 52 | .join("java"), 53 | _ => folder_path.join("bin").join("java"), 54 | }; 55 | 56 | if java_binary.exists() { 57 | // Check if the binary has execution permissions on linux and macOS 58 | #[cfg(unix)] 59 | { 60 | use std::os::unix::fs::PermissionsExt; 61 | 62 | let metadata = fs::metadata(&java_binary).await?; 63 | 64 | if !metadata.permissions().mode() & 0o111 != 0 { 65 | // try to change permissions 66 | let mut permissions = metadata.permissions(); 67 | permissions.set_mode(0o111); 68 | fs::set_permissions(&java_binary, permissions).await?; 69 | } 70 | } 71 | 72 | return Ok(java_binary.absolutize()?.to_path_buf()); 73 | } 74 | } 75 | 76 | Err(anyhow::anyhow!("Failed to find JRE")) 77 | } 78 | 79 | /// Download specific JRE to runtimes 80 | pub async fn jre_download( 81 | runtimes_folder: &Path, 82 | jre_distribution: &JavaDistribution, 83 | jre_version: &u32, 84 | on_progress: F, 85 | ) -> Result 86 | where 87 | F: Fn(u64, u64), 88 | { 89 | let runtime_path = 90 | runtimes_folder.join(format!("{}_{}", jre_distribution.get_name(), jre_version)); 91 | 92 | if runtime_path.exists() { 93 | fs::remove_dir_all(&runtime_path).await?; 94 | } 95 | fs::create_dir_all(&runtime_path).await?; 96 | 97 | let url = jre_distribution.get_url(jre_version)?; 98 | let retrieved_bytes = download_file(&url, on_progress).await?; 99 | let cursor = Cursor::new(&retrieved_bytes[..]); 100 | 101 | match OS { 102 | OperatingSystem::WINDOWS => zip_extract(cursor, runtime_path.as_path()).await?, 103 | OperatingSystem::LINUX | OperatingSystem::OSX => { 104 | tar_gz_extract(cursor, runtime_path.as_path()).await? 105 | } 106 | _ => bail!("Unsupported OS"), 107 | } 108 | 109 | // Find JRE afterwards 110 | find_java_binary(runtimes_folder, jre_distribution, jre_version).await 111 | } 112 | -------------------------------------------------------------------------------- /src-tauri/src/minecraft/java/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | 20 | mod distribution; 21 | pub(crate) mod jre_downloader; 22 | mod runtime; 23 | 24 | pub use {distribution::*, jre_downloader::*, runtime::*}; 25 | -------------------------------------------------------------------------------- /src-tauri/src/minecraft/java/runtime.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | 20 | use anyhow::{bail, Result}; 21 | use std::path::{Path, PathBuf}; 22 | use std::process::Stdio; 23 | use tokio::io::AsyncReadExt; 24 | use tokio::process::{Child, Command}; 25 | use tokio::sync::oneshot::Receiver; 26 | use tracing::debug; 27 | pub struct JavaRuntime(PathBuf); 28 | 29 | impl JavaRuntime { 30 | pub fn new(path: PathBuf) -> JavaRuntime { 31 | JavaRuntime(path) 32 | } 33 | 34 | pub async fn execute(&self, arguments: Vec, game_dir: &Path) -> Result { 35 | if !self.0.exists() { 36 | bail!("Java runtime not found at: {}", self.0.display()); 37 | } 38 | 39 | debug!("Executing Java runtime: {}", self.0.display()); 40 | 41 | let mut command = Command::new(&self.0); 42 | command.current_dir(game_dir); 43 | command.args(arguments); 44 | 45 | command.stderr(Stdio::piped()).stdout(Stdio::piped()); 46 | 47 | let child = command.spawn()?; 48 | Ok(child) 49 | } 50 | 51 | pub async fn handle_io( 52 | &self, 53 | running_task: &mut Child, 54 | on_stdout: fn(&D, &[u8]) -> Result<()>, 55 | on_stderr: fn(&D, &[u8]) -> Result<()>, 56 | terminator: Receiver<()>, 57 | data: &D, 58 | ) -> Result<()> { 59 | let mut stdout = running_task.stdout.take().unwrap(); 60 | let mut stderr = running_task.stderr.take().unwrap(); 61 | 62 | let mut stdout_buf = vec![0; 1024]; 63 | let mut stderr_buf = vec![0; 1024]; 64 | 65 | tokio::pin!(terminator); 66 | 67 | loop { 68 | tokio::select! { 69 | read_len = stdout.read(&mut stdout_buf) => { 70 | let _ = on_stdout(&data, &stdout_buf[..read_len?]); 71 | }, 72 | read_len = stderr.read(&mut stderr_buf) => { 73 | let _ = on_stderr(&data, &stderr_buf[..read_len?]); 74 | }, 75 | _ = &mut terminator => { 76 | running_task.kill().await?; 77 | break; 78 | }, 79 | exit_status = running_task.wait() => { 80 | let code = exit_status?.code().unwrap_or(7900); // 7900 = unwrap failed error code 81 | 82 | debug!("Process exited with code: {}", code); 83 | if code != 0 && code != -1073740791 { // -1073740791 = happens when the process is killed forcefully, we don't want to bail in this case 84 | bail!("Process exited with non-zero exit code: {}.", code); 85 | } 86 | break; 87 | }, 88 | } 89 | } 90 | Ok(()) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src-tauri/src/minecraft/launcher/assets.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | path::{Path, PathBuf}, 3 | sync::{ 4 | atomic::{AtomicU64, Ordering}, 5 | Arc, 6 | }, 7 | }; 8 | 9 | use anyhow::Result; 10 | use backon::{Retryable, ExponentialBuilder}; 11 | use futures::{stream, StreamExt}; 12 | use tracing::error; 13 | 14 | use crate::{ 15 | error::LauncherError, 16 | join_and_mkdir, 17 | minecraft::{ 18 | progress::{ProgressReceiver, ProgressUpdate, ProgressUpdateSteps}, 19 | version::{AssetIndexLocation, VersionProfile}, 20 | }, 21 | }; 22 | 23 | use super::{LauncherData, StartParameter}; 24 | 25 | pub async fn setup_assets<'a, D: Send + Sync>( 26 | assets_folder: &'a Path, 27 | version_profile: &'a VersionProfile, 28 | launching_parameter: &'a StartParameter, 29 | launcher_data: &'a LauncherData, 30 | ) -> Result<&'a AssetIndexLocation> { 31 | let indexes_folder: PathBuf = join_and_mkdir!(assets_folder, "indexes"); 32 | let objects_folder: PathBuf = join_and_mkdir!(assets_folder, "objects"); 33 | 34 | let asset_index_location = version_profile 35 | .asset_index_location 36 | .as_ref() 37 | .ok_or_else(|| { 38 | LauncherError::InvalidVersionProfile("Asset index unspecified".to_string()) 39 | })?; 40 | let asset_index = asset_index_location 41 | .load_asset_index(&indexes_folder) 42 | .await?; 43 | let asset_objects_to_download = asset_index 44 | .objects 45 | .values() 46 | .map(|x| x.to_owned()) 47 | .collect::>(); 48 | let assets_downloaded = Arc::new(AtomicU64::new(0)); 49 | let asset_max = asset_objects_to_download.len() as u64; 50 | 51 | launcher_data.progress_update(ProgressUpdate::set_label("Checking assets...")); 52 | launcher_data.progress_update(ProgressUpdate::set_for_step( 53 | ProgressUpdateSteps::DownloadAssets, 54 | 0, 55 | asset_max, 56 | )); 57 | 58 | let _: Vec> = 59 | stream::iter(asset_objects_to_download.into_iter().map(|asset_object| { 60 | let download_count = assets_downloaded.clone(); 61 | let folder_clone = objects_folder.clone(); 62 | 63 | async move { 64 | let hash = asset_object.hash.clone(); 65 | 66 | match (|| async { asset_object.download(folder_clone.clone(), launcher_data).await }) 67 | .retry(ExponentialBuilder::default()) 68 | .notify(|err, dur| { 69 | launcher_data.log(&format!( 70 | "Failed to download asset: {}. Retrying in {:?}. Error: {}", 71 | hash, dur, err 72 | )); 73 | }) 74 | .await 75 | { 76 | Ok(downloaded) => { 77 | let curr = download_count.fetch_add(1, Ordering::Relaxed); 78 | 79 | if downloaded { 80 | // the progress bar is only being updated when an asset has been downloaded to improve speeds 81 | launcher_data.progress_update(ProgressUpdate::set_for_step( 82 | ProgressUpdateSteps::DownloadAssets, 83 | curr, 84 | asset_max, 85 | )); 86 | } 87 | } 88 | Err(err) => { 89 | // We hope the asset was not important 90 | error!("Unable to download asset {}: {:?}", hash, err) 91 | }, 92 | } 93 | 94 | Ok(()) 95 | } 96 | })) 97 | .buffer_unordered(launching_parameter.concurrent_downloads as usize) 98 | .collect() 99 | .await; 100 | 101 | launcher_data.progress_update(ProgressUpdate::set_for_step( 102 | ProgressUpdateSteps::DownloadAssets, 103 | asset_max, 104 | asset_max, 105 | )); 106 | 107 | Ok(asset_index_location) 108 | } 109 | -------------------------------------------------------------------------------- /src-tauri/src/minecraft/launcher/client_jar.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{bail, Context, Result}; 2 | use path_absolutize::Absolutize; 3 | use std::fmt::Write; 4 | use std::path::Path; 5 | use tokio::fs; 6 | 7 | use crate::{ 8 | error::LauncherError, 9 | minecraft::{ 10 | progress::{get_max, get_progress, ProgressReceiver, ProgressUpdate, ProgressUpdateSteps}, 11 | version::VersionProfile, 12 | }, 13 | utils::{download_file, sha1sum, OS}, 14 | }; 15 | 16 | use super::LauncherData; 17 | 18 | pub async fn setup_client_jar( 19 | client_folder: &Path, 20 | natives_folder: &Path, 21 | version_profile: &VersionProfile, 22 | launcher_data: &LauncherData, 23 | class_path: &mut String, 24 | ) -> Result<()> { 25 | if let Some(client_download) = version_profile 26 | .downloads 27 | .as_ref() 28 | .and_then(|x| x.client.as_ref()) 29 | { 30 | let client_jar = client_folder.join(format!("{}.jar", &version_profile.id)); 31 | 32 | // Add client jar to class path 33 | write!( 34 | class_path, 35 | "{}{}", 36 | &client_jar.absolutize().unwrap().to_str().unwrap(), 37 | OS.get_path_separator()? 38 | )?; 39 | 40 | // Download client jar 41 | let requires_download = if !client_jar.exists() { 42 | true 43 | } else { 44 | let hash = sha1sum(&client_jar)?; 45 | launcher_data.log(&*format!( 46 | "Client JAR local hash: {}, remote: {}", 47 | hash, client_download.sha1 48 | )); 49 | hash != client_download.sha1 50 | }; 51 | 52 | if requires_download { 53 | launcher_data.log("Downloading client..."); 54 | launcher_data.progress_update(ProgressUpdate::set_label("Downloading client...")); 55 | 56 | let retrieved_bytes = download_file(&client_download.url, |a, b| { 57 | launcher_data.progress_update(ProgressUpdate::set_for_step( 58 | ProgressUpdateSteps::DownloadClientJar, 59 | get_progress(0, a, b), 60 | get_max(1), 61 | )); 62 | }) 63 | .await?; 64 | 65 | fs::write(&client_jar, retrieved_bytes) 66 | .await 67 | .context("Failed to write client JAR")?; 68 | 69 | // After downloading, check sha1 70 | let hash = sha1sum(&client_jar)?; 71 | launcher_data.log(&*format!( 72 | "Client JAR local hash: {}, remote: {}", 73 | hash, client_download.sha1 74 | )); 75 | if hash != client_download.sha1 { 76 | bail!("Client JAR download failed. SHA1 mismatch."); 77 | } 78 | } 79 | 80 | // Natives folder 81 | if !natives_folder.exists() { 82 | fs::create_dir_all(&natives_folder) 83 | .await 84 | .context("Failed to create natives folder")?; 85 | } 86 | } else { 87 | return Err(LauncherError::InvalidVersionProfile( 88 | "No client JAR downloads were specified.".to_string(), 89 | ) 90 | .into()); 91 | } 92 | 93 | Ok(()) 94 | } 95 | -------------------------------------------------------------------------------- /src-tauri/src/minecraft/launcher/jre.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, PathBuf}; 2 | 3 | use anyhow::{anyhow, Result}; 4 | 5 | use super::{LauncherData, StartParameter}; 6 | use crate::app::client_api::LaunchManifest; 7 | use crate::minecraft::java::DistributionSelection; 8 | use crate::minecraft::{ 9 | java::{find_java_binary, jre_downloader}, 10 | progress::{get_max, get_progress, ProgressReceiver, ProgressUpdate, ProgressUpdateSteps}, 11 | }; 12 | 13 | pub async fn load_jre( 14 | runtimes_folder: &Path, 15 | manifest: &LaunchManifest, 16 | launching_parameter: &StartParameter, 17 | launcher_data: &LauncherData, 18 | ) -> Result { 19 | let distribution = match &launching_parameter.java_distribution { 20 | DistributionSelection::Automatic(_) => &manifest.build.jre_distribution, 21 | DistributionSelection::Custom(path) => return Ok(PathBuf::from(path)), 22 | DistributionSelection::Manual(distribution) => distribution, 23 | }; 24 | 25 | // Check if distribution supports JRE version 26 | if !distribution.supports_version(manifest.build.jre_version) { 27 | return Err(anyhow!( 28 | "The selected JRE distribution does not support the required version of Java." 29 | )); 30 | } 31 | 32 | launcher_data.progress_update(ProgressUpdate::set_label("Checking for JRE...")); 33 | 34 | if let Ok(path) = 35 | find_java_binary(runtimes_folder, distribution, &manifest.build.jre_version).await 36 | { 37 | return Ok(path); 38 | } 39 | 40 | launcher_data.log("Downloading JRE..."); 41 | launcher_data.progress_update(ProgressUpdate::set_label("Download JRE...")); 42 | 43 | jre_downloader::jre_download( 44 | &runtimes_folder, 45 | distribution, 46 | &manifest.build.jre_version, 47 | |a, b| { 48 | launcher_data.progress_update(ProgressUpdate::set_for_step( 49 | ProgressUpdateSteps::DownloadJRE, 50 | get_progress(0, a, b), 51 | get_max(1), 52 | )); 53 | }, 54 | ) 55 | .await 56 | } 57 | -------------------------------------------------------------------------------- /src-tauri/src/minecraft/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | 20 | pub mod auth; 21 | pub mod java; 22 | pub mod launcher; 23 | pub mod prelauncher; 24 | pub mod progress; 25 | mod rule_interpreter; 26 | pub mod version; 27 | -------------------------------------------------------------------------------- /src-tauri/src/minecraft/progress.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | 20 | use serde::Serialize; 21 | 22 | #[derive(Debug)] 23 | pub enum ProgressUpdateSteps { 24 | DownloadLiquidBounceMods, 25 | DownloadJRE, 26 | DownloadClientJar, 27 | DownloadLibraries, 28 | DownloadAssets, 29 | } 30 | 31 | pub fn get_progress(idx: usize, curr: u64, max: u64) -> u64 { 32 | idx as u64 * 100 + (curr * 100 / max.max(1)) 33 | } 34 | 35 | pub fn get_max(len: usize) -> u64 { 36 | len as u64 * 100 37 | } 38 | 39 | impl ProgressUpdateSteps { 40 | fn len() -> usize { 41 | 5 42 | } 43 | 44 | fn step_idx(&self) -> usize { 45 | match self { 46 | ProgressUpdateSteps::DownloadLiquidBounceMods => 0, 47 | ProgressUpdateSteps::DownloadJRE => 1, 48 | ProgressUpdateSteps::DownloadClientJar => 2, 49 | ProgressUpdateSteps::DownloadLibraries => 3, 50 | ProgressUpdateSteps::DownloadAssets => 4, 51 | } 52 | } 53 | } 54 | 55 | #[derive(Debug, Serialize, Clone)] 56 | #[serde(tag = "type", content = "value")] 57 | pub enum ProgressUpdate { 58 | #[serde(rename = "max")] 59 | SetMax(u64), 60 | #[serde(rename = "progress")] 61 | SetProgress(u64), 62 | #[serde(rename = "label")] 63 | SetLabel(String), 64 | } 65 | 66 | const PER_STEP: u64 = 1024; 67 | 68 | impl ProgressUpdate { 69 | pub fn set_for_step(step: ProgressUpdateSteps, progress: u64, max: u64) -> Self { 70 | Self::SetProgress(step.step_idx() as u64 * PER_STEP + (progress * PER_STEP / max)) 71 | } 72 | pub fn set_to_max() -> Self { 73 | Self::SetProgress(ProgressUpdateSteps::len() as u64 * PER_STEP) 74 | } 75 | pub fn set_max() -> Self { 76 | let max = ProgressUpdateSteps::len() as u64; 77 | 78 | Self::SetMax(max * PER_STEP) 79 | } 80 | pub fn set_label>(str: S) -> Self { 81 | Self::SetLabel(str.as_ref().to_owned()) 82 | } 83 | } 84 | 85 | pub trait ProgressReceiver { 86 | fn progress_update(&self, update: ProgressUpdate); 87 | fn log(&self, msg: &str); 88 | } 89 | -------------------------------------------------------------------------------- /src-tauri/src/minecraft/rule_interpreter.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | 20 | use std::collections::HashSet; 21 | 22 | use anyhow::Result; 23 | use regex::Regex; 24 | 25 | use crate::minecraft::version::{Rule, RuleAction}; 26 | use crate::utils::{ARCHITECTURE, OS, OS_VERSION}; 27 | 28 | pub fn check_condition(rules: &Vec, features: &HashSet) -> Result { 29 | if rules.is_empty() { 30 | return Ok(true); 31 | } 32 | 33 | let os_name = OS.get_simple_name()?; 34 | let os_version = &*OS_VERSION.clone(); 35 | 36 | let mut allow = false; 37 | 38 | for rule in rules { 39 | let mut rule_applies = true; 40 | 41 | if let Some(os_requirement) = &rule.os { 42 | if os_requirement.name.as_ref().map_or(false, |x| x != os_name) { 43 | rule_applies = false; 44 | } 45 | if let Some(arch) = &os_requirement.arch { 46 | if *arch != *ARCHITECTURE { 47 | rule_applies = false; 48 | } 49 | } 50 | if let Some(version_regex) = &os_requirement.version { 51 | if !Regex::new(version_regex)?.is_match(&os_version) { 52 | rule_applies = false; 53 | } 54 | } 55 | } 56 | if let Some(filtered_features) = &rule.features { 57 | for (feature_name, feature_supported) in filtered_features { 58 | if features.contains(feature_name) != *feature_supported { 59 | rule_applies = false; 60 | } 61 | } 62 | } 63 | 64 | if rule_applies { 65 | match rule.action { 66 | RuleAction::Allow => allow = true, 67 | RuleAction::Disallow => allow = false, 68 | } 69 | } 70 | } 71 | 72 | Ok(allow) 73 | } 74 | -------------------------------------------------------------------------------- /src-tauri/src/utils/checksum.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | 20 | use anyhow::Result; 21 | use sha1::{Digest, Sha1}; 22 | use std::path::PathBuf; 23 | 24 | pub fn sha1sum(path: &PathBuf) -> Result { 25 | // get sha1 of library file and check if it matches 26 | let mut file = std::fs::File::open(path)?; 27 | let mut hasher = Sha1::new(); 28 | std::io::copy(&mut file, &mut hasher)?; 29 | let hash = hasher.finalize(); 30 | let hex_hash = base16ct::lower::encode_string(&hash); 31 | 32 | Ok(hex_hash) 33 | } 34 | -------------------------------------------------------------------------------- /src-tauri/src/utils/download.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | 20 | use std::path::Path; 21 | 22 | use crate::error::map_into_connection_error; 23 | use crate::HTTP_CLIENT; 24 | use anyhow::Result; 25 | use backon::{ExponentialBuilder, Retryable}; 26 | use tokio::fs; 27 | use tracing::{debug, warn}; 28 | 29 | /// Download a file using HTTP_CLIENT without any progress tracking 30 | pub async fn download_file_untracked(url: &str, path: impl AsRef) -> Result<()> { 31 | let path = path.as_ref().to_owned(); 32 | let response = HTTP_CLIENT.get(url).send().await?.error_for_status()?; 33 | 34 | let content = response.bytes().await?; 35 | fs::write(path, content).await?; 36 | Ok(()) 37 | } 38 | 39 | pub async fn download_file(url: &str, on_progress: F) -> Result> 40 | where 41 | F: Fn(u64, u64), 42 | { 43 | debug!("Downloading file {:?}", url); 44 | 45 | let mut response = (|| async { 46 | HTTP_CLIENT 47 | .get(url.trim()) 48 | .send() 49 | .await? 50 | .error_for_status() 51 | }).retry(ExponentialBuilder::default()) 52 | .notify(|err, dur| { 53 | warn!("Failed to download file {}. Retrying in {:?}. Error: {}", url, dur, err); 54 | }) 55 | .await 56 | .map_err(|e| map_into_connection_error(e.into()))?; 57 | 58 | debug!("Response received from url"); 59 | 60 | let max_len = response.content_length().unwrap_or(0); 61 | let mut output = Vec::with_capacity(max_len as usize); 62 | let mut curr_len = 0; 63 | 64 | on_progress(0, max_len); 65 | 66 | debug!("Reading data from response chunk..."); 67 | while let Some(data) = response.chunk().await 68 | .map_err(|e| map_into_connection_error(e.into()))? { 69 | output.extend_from_slice(&data); 70 | curr_len += data.len(); 71 | on_progress(curr_len as u64, max_len); 72 | } 73 | 74 | debug!("Downloaded file"); 75 | Ok(output) 76 | } 77 | 78 | -------------------------------------------------------------------------------- /src-tauri/src/utils/extract.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | 20 | use anyhow::{Context, Result}; 21 | use async_compression::tokio::bufread::GzipDecoder; 22 | use async_zip::read::seek::ZipFileReader; 23 | use std::path::{Path, PathBuf}; 24 | use tokio::fs::{create_dir_all, OpenOptions}; 25 | use tokio::io; 26 | use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeek, BufReader}; 27 | 28 | /// Extracts everything from the ZIP archive to the output directory 29 | /// 30 | /// Taken from https://github.com/Majored/rs-async-zip/blob/main/examples/file_extraction.rs 31 | pub async fn zip_extract(archive: R, out_dir: &Path) -> Result<()> 32 | where 33 | R: AsyncRead + AsyncSeek + Unpin, 34 | { 35 | let mut reader = ZipFileReader::new(archive).await?; 36 | for index in 0..reader.file().entries().len() { 37 | let entry = &reader.file().entries().get(index).unwrap().entry(); 38 | let file_name = entry.filename(); 39 | 40 | let path = out_dir.join(sanitize_file_path(file_name)); 41 | // If the filename of the entry ends with '/', it is treated as a directory. 42 | // This is implemented by previous versions of this crate and the Python Standard Library. 43 | // https://docs.rs/async_zip/0.0.8/src/async_zip/read/mod.rs.html#63-65 44 | // https://github.com/python/cpython/blob/820ef62833bd2d84a141adedd9a05998595d6b6d/Lib/zipfile.py#L528 45 | let entry_is_dir = file_name.ends_with('/'); 46 | 47 | let mut entry_reader = reader.entry(index).await?; 48 | 49 | if entry_is_dir { 50 | // The directory may have been created if iteration is out of order. 51 | if !path.exists() { 52 | create_dir_all(&path).await?; 53 | } 54 | } else { 55 | // Creates parent directories. They may not exist if iteration is out of order 56 | // or the archive does not contain directory entries. 57 | let parent = path.parent().unwrap(); 58 | if !parent.is_dir() { 59 | create_dir_all(parent).await?; 60 | } 61 | 62 | let mut writer = OpenOptions::new() 63 | .write(true) 64 | .create(true) 65 | .open(&path) 66 | .await 67 | .context("Failed to create extracted file")?; 68 | io::copy(&mut entry_reader, &mut writer).await?; 69 | } 70 | } 71 | Ok(()) 72 | } 73 | 74 | pub async fn tar_gz_extract(archive: R, out_dir: &Path) -> Result<()> 75 | where 76 | R: AsyncRead + AsyncSeek + Unpin, 77 | { 78 | let mut decoder = GzipDecoder::new(BufReader::new(archive)); 79 | let mut decoded_data: Vec = vec![]; 80 | decoder.read_to_end(&mut decoded_data).await?; 81 | 82 | let mut ar = tokio_tar::Archive::new(&decoded_data[..]); 83 | ar.unpack(out_dir).await?; 84 | Ok(()) 85 | } 86 | 87 | /// Returns a relative path without reserved names, redundant separators, ".", or "..". 88 | fn sanitize_file_path(path: &str) -> PathBuf { 89 | // Replaces backwards slashes 90 | path.replace('\\', "/") 91 | // Sanitizes each component 92 | .split('/') 93 | .map(sanitize_filename::sanitize) 94 | .collect() 95 | } 96 | -------------------------------------------------------------------------------- /src-tauri/src/utils/hosts.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | use anyhow::{bail, Context, Result}; 4 | use tokio::fs; 5 | 6 | const HOSTS_PATH: &str = "Windows\\System32\\drivers\\etc\\hosts"; 7 | const HOSTS: [&str; 4] = [ 8 | "mojang.com", 9 | "minecraft.net", 10 | "liquidbounce.net", 11 | "ccbluex.net", 12 | ]; 13 | 14 | /// We have noticed many user have modified the hosts file to block the Minecraft authentication server. 15 | /// This is likely by using a third-party program. Because LiquidLauncher requires access to the authentication server, we have to modify the hosts file to allow access. 16 | /// we need to check the hosts file and alert the user if it has been modified. 17 | pub async fn check_hosts_file() -> Result<()> { 18 | // Get location of Windows hosts file dynamically 19 | // "SystemDrive" env, if not assigned default to C: 20 | let system_drive = env::var("SystemDrive").unwrap_or("C:".to_string()); 21 | let hosts_path = format!("{}\\{}", system_drive, HOSTS_PATH); 22 | 23 | // Check if hosts file exists, if not cancel this check with OK 24 | if let Ok(exists) = fs::try_exists(&hosts_path).await { 25 | if !exists { 26 | return Ok(()); 27 | } 28 | } 29 | 30 | // Check if the hosts file has been modified 31 | let hosts_file = fs::read_to_string(&hosts_path) 32 | .await 33 | .context(format!("Failed to read hosts file at {}", hosts_path))?; 34 | 35 | let flagged_entries = hosts_file 36 | .lines() 37 | .filter(|line| { 38 | if line.starts_with('#') { 39 | return false; 40 | } 41 | 42 | let mut parts = line.split_whitespace(); 43 | let _ = match parts.next() { 44 | Some(ip) => ip, 45 | None => return false, 46 | }; 47 | let domain = match parts.next() { 48 | Some(domain) => domain, 49 | None => return false, 50 | }; 51 | 52 | HOSTS.iter().any(|&entry| domain.contains(entry)) 53 | }) 54 | .collect::>(); 55 | 56 | if !flagged_entries.is_empty() { 57 | bail!( 58 | "The hosts file has been modified to block the Minecraft authentication server.\n\ 59 | \n\ 60 | Please remove the following entries from the hosts file:\n\ 61 | {}\n\n\ 62 | The file is located at:\n\ 63 | {}", 64 | flagged_entries.join("\n"), 65 | hosts_path 66 | ); 67 | } 68 | 69 | Ok(()) 70 | } 71 | -------------------------------------------------------------------------------- /src-tauri/src/utils/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! join_and_mkdir { 3 | ($path:expr, $join:expr) => {{ 4 | let path = $path.join($join); 5 | $crate::mkdir!(&path); 6 | path 7 | }}; 8 | } 9 | 10 | #[macro_export] 11 | macro_rules! join_and_mkdir_vec { 12 | ($path:expr, $joins:expr) => {{ 13 | let mut path = $path.to_path_buf(); 14 | for join in $joins { 15 | path = path.join(join); 16 | $crate::mkdir!(&path); 17 | } 18 | path 19 | }}; 20 | } 21 | 22 | #[macro_export] 23 | macro_rules! mkdir { 24 | ($path:expr) => { 25 | if !$path.exists() { 26 | if let Err(e) = std::fs::create_dir_all(&$path) { 27 | error!("Failed to create directory {:?}: {}", $path, e); 28 | } 29 | } 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /src-tauri/src/utils/maven.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | 20 | use crate::error::LauncherError; 21 | use anyhow::Result; 22 | 23 | pub fn get_maven_artifact_path(artifact_id: &String) -> Result { 24 | let split = artifact_id.split(':').collect::>(); 25 | 26 | if split.len() != 3 { 27 | return Err(LauncherError::InvalidVersionProfile(format!( 28 | "Invalid artifact name: {}", 29 | artifact_id 30 | )) 31 | .into()); 32 | } 33 | 34 | Ok(format!( 35 | "{}/{name}/{ver}/{name}-{ver}.jar", 36 | split[0].replace('.', "/"), 37 | name = split[1], 38 | ver = split[2] 39 | )) 40 | } 41 | -------------------------------------------------------------------------------- /src-tauri/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of LiquidLauncher (https://github.com/CCBlueX/LiquidLauncher) 3 | * 4 | * Copyright (c) 2015 - 2024 CCBlueX 5 | * 6 | * LiquidLauncher is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * LiquidLauncher is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with LiquidLauncher. If not, see . 18 | */ 19 | 20 | mod checksum; 21 | mod download; 22 | mod extract; 23 | #[cfg(windows)] 24 | mod hosts; 25 | mod macros; 26 | mod maven; 27 | mod sys; 28 | 29 | pub use {checksum::*, download::*, extract::*, maven::*, sys::*}; 30 | 31 | #[cfg(windows)] 32 | pub use hosts::*; 33 | -------------------------------------------------------------------------------- /src-tauri/static/success.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Authentication Successful 7 | 26 | 27 | 28 |
29 |

Authentication Successful

30 |

You have successfully authenticated. You can close this tab now.

31 |
32 | 33 | -------------------------------------------------------------------------------- /src-tauri/tauri.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "productName": "LiquidLauncher", 3 | "version": "0.5.0", 4 | "identifier": "net.ccbluex.liquidlauncher", 5 | "build": { 6 | "beforeBuildCommand": "npm run build", 7 | "beforeDevCommand": "npm run dev", 8 | "devUrl": "http://localhost:5173", 9 | "frontendDist": "../dist" 10 | }, 11 | "app": { 12 | "withGlobalTauri": true, 13 | "windows": [ 14 | { 15 | "title": "LiquidLauncher", 16 | "label": "main", 17 | "width": 1000, 18 | "minWidth": 1000, 19 | "height": 670, 20 | "minHeight": 670, 21 | "resizable": true, 22 | "fullscreen": false, 23 | "transparent": true, 24 | "decorations": false, 25 | "center": true, 26 | "windowEffects": { 27 | "effects": [ 28 | "acrylic" 29 | ] 30 | }, 31 | "shadow": true 32 | } 33 | ] 34 | }, 35 | "bundle": { 36 | "active": true, 37 | "category": "Game", 38 | "copyright": "CCBlueX", 39 | "publisher": "CCBlueX", 40 | "externalBin": [], 41 | "icon": [ 42 | "icons/32x32.png", 43 | "icons/128x128.png", 44 | "icons/128x128@2x.png", 45 | "icons/icon.icns", 46 | "icons/icon.ico" 47 | ], 48 | "shortDescription": "A LiquidBounce launcher for Minecraft", 49 | "longDescription": "A LiquidBounce hacked-client launcher for the game Minecraft", 50 | "license": "GPL-3.0", 51 | "licenseFile": "../LICENSE", 52 | "resources": [], 53 | "targets": "all", 54 | "createUpdaterArtifacts": "v1Compatible", 55 | "windows": { 56 | "certificateThumbprint": null, 57 | "digestAlgorithm": "sha256", 58 | "timestampUrl": "", 59 | "webviewInstallMode": { 60 | "type": "embedBootstrapper" 61 | } 62 | }, 63 | "linux": { 64 | "appimage": { 65 | "bundleMediaFramework": true 66 | } 67 | }, 68 | "macOS": { 69 | "entitlements": null, 70 | "exceptionDomain": "", 71 | "frameworks": [], 72 | "providerShortName": null, 73 | "signingIdentity": null 74 | } 75 | }, 76 | "plugins": { 77 | "updater": { 78 | "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEU1QkM5MTlDREQ4NkEzNjMKUldSam80YmRuSkc4NVNxS2MzYVZPN1I4a283RkdsS0lPTjVjRWlxQ1JhdzFZUkRtc0c1eEtqSzQK", 79 | "endpoints": [ 80 | "https://github.com/CCBlueX/LiquidLauncher/releases/latest/download/latest.json", 81 | "https://api.liquidbounce.net/api/v1/launcher/releases/{{target}}/{{current_version}}" 82 | ] 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /src/App.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 | 7 |
8 | -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Inter"; 3 | font-weight: 600; 4 | src: url("font/Inter-Bold.ttf"); 5 | } 6 | 7 | @font-face { 8 | font-family: "Inter"; 9 | font-weight: 500; 10 | src: url("font/Inter-Medium.ttf"); 11 | } 12 | 13 | @font-face { 14 | font-family: "Inter"; 15 | font-weight: 400; 16 | src: url("font/Inter-Regular.ttf"); 17 | } 18 | 19 | * { 20 | box-sizing: border-box; 21 | margin: 0; 22 | padding: 0; 23 | -webkit-appearance: none; 24 | } 25 | 26 | *:focus { 27 | outline: none; 28 | } 29 | 30 | body { 31 | background: transparent; 32 | font-family: "Inter", sans-serif; 33 | font-weight: 400; 34 | font-size: 14px; 35 | user-select: none; 36 | /* border-radius: 14px; */ 37 | height: 100%; 38 | overflow: hidden; 39 | } 40 | 41 | ::-webkit-scrollbar { 42 | width: 3px; 43 | } 44 | 45 | ::-webkit-scrollbar-thumb { 46 | background-color: #4677FF; 47 | border-radius: 5px; 48 | } -------------------------------------------------------------------------------- /src/font/Inter-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src/font/Inter-Bold.ttf -------------------------------------------------------------------------------- /src/font/Inter-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src/font/Inter-Medium.ttf -------------------------------------------------------------------------------- /src/font/Inter-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCBlueX/LiquidLauncher/4ac8ab3c458ebe4706868de4fa05543c4756568b/src/font/Inter-Regular.ttf -------------------------------------------------------------------------------- /src/font/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /src/lib/Window.svelte: -------------------------------------------------------------------------------- 1 | 98 | 99 |
100 |
101 | 102 | {#if error} 103 | 104 | {:else if loading || !client} 105 | 106 | {:else if client && !client.is_secure && !allowNonSecure} 107 | { 109 | allowNonSecure = true; 110 | }} 111 | on:cancel={async () => { 112 | await exit(0); 113 | }} 114 | /> 115 | {:else if options} 116 | {#if options.start.account} 117 | 118 | {:else} 119 | 120 | {/if} 121 | {/if} 122 |
123 | 124 | -------------------------------------------------------------------------------- /src/lib/common/ButtonClose.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /src/lib/common/ButtonCopyClipboard.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 | 29 | 30 | -------------------------------------------------------------------------------- /src/lib/common/Logo.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/lib/common/TitleBar.svelte: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | -------------------------------------------------------------------------------- /src/lib/common/ToolTip.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 |
22 | {#if shown} 23 |
24 | {text} 25 |
26 | {/if} 27 |
28 | 29 | 56 | -------------------------------------------------------------------------------- /src/lib/common/VerticalFlexWrapper.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 | 7 |
8 | 9 | 23 | -------------------------------------------------------------------------------- /src/lib/common/social/ButtonIcon.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 19 | 20 | -------------------------------------------------------------------------------- /src/lib/common/social/ButtonIconText.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 19 | 20 | -------------------------------------------------------------------------------- /src/lib/common/social/SocialBar.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 14 | 15 | 27 | -------------------------------------------------------------------------------- /src/lib/login/Facts.svelte: -------------------------------------------------------------------------------- 1 | 63 | 64 |
65 | {#key currentFact} 66 |
67 |
{facts[currentFact].title}
68 |
{facts[currentFact].description}
69 |
70 | 77 | 84 |
85 |
86 | {/key} 87 |
88 | 89 | 138 | -------------------------------------------------------------------------------- /src/lib/login/LoginScreen.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 |
24 | 25 | 26 |
27 | 28 | -------------------------------------------------------------------------------- /src/lib/login/loginmodal/LoginModal.svelte: -------------------------------------------------------------------------------- 1 | 53 | 54 | 69 | 70 | -------------------------------------------------------------------------------- /src/lib/login/loginmodal/ModalButton.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/login/loginmodal/ModalInput.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 |
10 |
11 | {icon} 12 |
13 | 14 |
15 | 16 | -------------------------------------------------------------------------------- /src/lib/main/Account.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 23 | 24 | -------------------------------------------------------------------------------- /src/lib/main/ButtonLaunchArea.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/lib/main/ButtonVersion.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /src/lib/main/ContentWrapper.svelte: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 17 | -------------------------------------------------------------------------------- /src/lib/main/ErrorScreen.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |

Error Occurred

24 |

{error.message}

25 | 26 | {#if error.error} 27 |
28 |
29 |

Technical Details:

30 |
31 | 32 |
33 |
34 |
{error.error}
35 |
36 | {/if} 37 | 38 |
39 | openUrl('https://liquidbounce.net/docs/Tutorials/Fixing%20LiquidLauncher')}> 40 | openUrl('https://ccbluex.net/contact')}> 41 |
42 |
43 |
44 | 45 | -------------------------------------------------------------------------------- /src/lib/main/FirstRunWarning.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | dispatch('hide')} 14 | > 15 | 18 | 19 | openUrl("https://liquidbounce.net/docs/get-started")} 22 | color="#B83529" 23 | /> 24 | 25 | dispatch('continue')} 28 | color="#4677FF" 29 | /> 30 | -------------------------------------------------------------------------------- /src/lib/main/LaunchArea.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 |
24 |
25 | 29 |
30 | 31 |
 32 |         {@html parseChangelog(versionInfo.description)}
 33 |     
34 | 35 |
36 | dispatch("showVersionSelect")} /> 37 | dispatch("showVersionSelect")} /> 38 |
39 | 40 | {#if running} 41 |
42 | dispatch("terminate")} /> 43 | dispatch("showClientLog")} /> 44 |
45 | {:else} 46 | dispatch("launch")} /> 47 | {/if} 48 |
49 | 50 | -------------------------------------------------------------------------------- /src/lib/main/LoadingScreen.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 |
18 |
19 | 20 | -------------------------------------------------------------------------------- /src/lib/main/MainHeader.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 18 | {#if !running} 19 | 22 | {:else} 23 | 24 | {/if} 25 | 26 | 32 | 33 | -------------------------------------------------------------------------------- /src/lib/main/NonSecureConnectionScreen.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |

Security Warning

29 |

30 | LiquidLauncher is unable to connect to the secure (HTTPS) API endpoint, 31 | but was able to establish a connection to a non-secure (HTTP) endpoint. 32 |

33 | 34 |
35 |

Security Risks:

36 |
    37 |
  • Your connection is not encrypted and may be visible to others on your network
  • 38 |
  • Data sent and received could be intercepted or modified by malicious actors
  • 39 |
  • Your login credentials could be compromised if transmitted over this connection
  • 40 |
41 | 42 |

Recommendation:

43 |

44 | This issue is often caused by network restrictions or SSL certificate problems. 45 | We recommend using a VPN such as Cloudflare WARP to establish a secure connection. 46 |

47 |
48 | 49 |
50 | openUrl('https://1.1.1.1/')}> 51 | 52 | 53 |
54 |
55 |
56 | 57 | 114 | -------------------------------------------------------------------------------- /src/lib/main/VersionSelect.svelte: -------------------------------------------------------------------------------- 1 | 63 | 64 | dispatch('hide')} 67 | > 68 | ({ 71 | value: e, 72 | text: `${e.charAt(0).toUpperCase()}${e.slice(1)} ${e === "legacy" ? "(unsupported)" : ""}` 73 | }))} 74 | bind:value={options.version.branchName} 75 | on:change={() => dispatch('updateData')} 76 | /> 77 | ({ 82 | value: e.buildId, 83 | text: `${e.lbVersion} git-${e.commitId.substring(0, 7)} - ${e.date}` 84 | })) 85 | ]} 86 | bind:value={options.version.buildId} 87 | on:change={() => dispatch('updateData')} 88 | /> 89 | dispatch('updateData')} 94 | /> 95 | 96 | {#each versionState.recommendedMods as mod} 97 | dispatch('updateModStates')} 102 | /> 103 | {/each} 104 | 105 | 106 |
107 | 112 |
113 | {#each versionState.customMods as mod} 114 | dispatch('updateModStates')} 118 | on:delete={deleteMod} 119 | /> 120 | {/each} 121 |
122 |
-------------------------------------------------------------------------------- /src/lib/main/VersionWarning.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | dispatch('hide')} 15 | > 16 | 19 | dispatch('switchToNextgen')} 22 | color="#4677FF" 23 | /> 24 | 0} 26 | text="Launch anyway{launchVersionWarningCountdown > 0 ? ` (${launchVersionWarningCountdown})` : ''}" 27 | on:click={() => dispatch('runClientAnyway')} 28 | color="#B83529" 29 | /> 30 | -------------------------------------------------------------------------------- /src/lib/main/log/ClientLog.svelte: -------------------------------------------------------------------------------- 1 | 40 | 41 |
42 |
43 |
Client log
44 | 47 |
48 | 49 |
50 | 51 | 52 | 53 |
54 | 55 |
56 | 57 | 58 |
59 |
60 | 61 | 109 | -------------------------------------------------------------------------------- /src/lib/main/log/LogMessage.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
{text}
6 | 7 | -------------------------------------------------------------------------------- /src/lib/main/log/VirtualList.svelte: -------------------------------------------------------------------------------- 1 | 2 | 106 | 107 | 121 | 122 | 128 | 132 | {#each visible as row (row.index)} 133 | 134 | Missing template 135 | 136 | {/each} 137 | 138 | -------------------------------------------------------------------------------- /src/lib/main/news/News.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 |
17 | 21 |
22 |
23 |
{title}
24 |
{new Date(date).toLocaleDateString()}
25 |
26 |
{description}
27 | 28 |
29 |
30 | 31 | -------------------------------------------------------------------------------- /src/lib/main/news/NewsArea.svelte: -------------------------------------------------------------------------------- 1 | 18 | 19 |
20 |
21 | {#each news as n} 22 | 23 | {/each} 24 |
25 | 26 | 29 | 30 | 33 |
34 | 35 | 62 | -------------------------------------------------------------------------------- /src/lib/main/settings/GeneralSettings.svelte: -------------------------------------------------------------------------------- 1 | 59 | 60 | 70 | 71 | {#if options.start.javaDistribution.type === "manual"} 72 | 80 | {/if} 81 | 82 | {#if options.start.javaDistribution.type === "custom"} 83 | 90 | {/if} 91 | 92 | 98 | 99 | 107 | 108 | 116 | 117 | 122 | 123 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /src/lib/main/settings/PremiumSettings.svelte: -------------------------------------------------------------------------------- 1 | 30 | 31 | 36 | 37 | {#if options.premium.account} 38 | 39 | 40 | 41 | 42 | {#if !options.premium.account.premium} 43 | 46 | {/if} 47 | 48 | openUrl("https://user.liquidbounce.net")} 51 | color="#4677FF" 52 | /> 53 | 58 | {:else} 59 | 62 | 63 | 68 | {/if} -------------------------------------------------------------------------------- /src/lib/main/settings/Settings.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | dispatch('hide')} 18 | > 19 | 24 | 25 | {#if activeSettingsTab === "General"} 26 | 29 | {:else if activeSettingsTab === "Premium"} 30 | 34 | {/if} 35 | -------------------------------------------------------------------------------- /src/lib/main/statusbar/ProgressStatus.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 |
8 |
{text}
9 | 10 |
11 | 12 | 43 | -------------------------------------------------------------------------------- /src/lib/main/statusbar/StatusBar.svelte: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 16 | -------------------------------------------------------------------------------- /src/lib/main/statusbar/TextStatus.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
{text}
6 | 7 | -------------------------------------------------------------------------------- /src/lib/settings/ButtonSetting.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/lib/settings/CustomModSetting.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 | 13 | 16 |
17 | 18 | -------------------------------------------------------------------------------- /src/lib/settings/Description.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 | {description} 7 |
8 | 9 | -------------------------------------------------------------------------------- /src/lib/settings/DirectorySelectorSetting.svelte: -------------------------------------------------------------------------------- 1 | 27 | 28 |
29 |
{title}
30 |
31 | 32 | 35 | 38 |
39 |
40 | 41 | -------------------------------------------------------------------------------- /src/lib/settings/FileSelectorSetting.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 |
26 |
{title}
27 |
28 | 29 | 32 |
33 |
34 | 35 | -------------------------------------------------------------------------------- /src/lib/settings/IconButtonSetting.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /src/lib/settings/LauncherVersion.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
LiquidLauncher {version}
6 | 7 | -------------------------------------------------------------------------------- /src/lib/settings/LiquidBounceAccount.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 21 | 22 | -------------------------------------------------------------------------------- /src/lib/settings/RangeSetting.svelte: -------------------------------------------------------------------------------- 1 | 58 | 59 |
60 |
{title}
61 |
62 | 69 | {valueSuffix} 70 |
71 |
72 |
73 | 74 | 112 | -------------------------------------------------------------------------------- /src/lib/settings/RangeSettingStyles.css: -------------------------------------------------------------------------------- 1 | .range-setting .noUi-handle { 2 | background-color: #4677ff; 3 | box-shadow: unset; 4 | border: none; 5 | border-radius: 50%; 6 | height: 12px !important; 7 | width: 12px !important; 8 | transform: translate(-10px, 1px); 9 | } 10 | 11 | .range-setting .noUi-handle::after, 12 | .range-setting .noUi-handle::before { 13 | display: none; 14 | } 15 | 16 | .range-setting .noUi-target { 17 | margin: 10px 0; 18 | height: 2px; 19 | border: none; 20 | background-color: rgba(0, 0, 0, 0.5); 21 | box-shadow: unset; 22 | } 23 | 24 | .range-setting .noUi-connect { 25 | background-color: #4677ff; 26 | } -------------------------------------------------------------------------------- /src/lib/settings/SelectSetting.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 |
{title}
13 | 18 |
19 | 20 | -------------------------------------------------------------------------------- /src/lib/settings/SettingWrapper.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 |
7 | {title} 8 | 9 |
10 |
11 | 12 |
13 |
14 | 15 | -------------------------------------------------------------------------------- /src/lib/settings/SettingsContainer.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
15 |
16 |
{title}
17 | 20 |
21 | 22 | 23 | 24 |
25 | 26 |
27 |
28 | 29 | -------------------------------------------------------------------------------- /src/lib/settings/TextSetting.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 |
8 |
{title}
9 | 10 |
11 | 12 | -------------------------------------------------------------------------------- /src/lib/settings/ToggleSetting.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 17 | 18 | 80 | -------------------------------------------------------------------------------- /src/lib/settings/tab/Tab.svelte: -------------------------------------------------------------------------------- 1 |
2 | 3 |
-------------------------------------------------------------------------------- /src/lib/settings/tab/Tabs.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | {#each tabs as t} 8 | 9 | {/each} 10 |
11 | 12 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import "./app.css"; 2 | import App from "./App.svelte"; 3 | 4 | const app = new App({ 5 | target: document.getElementById("app"), 6 | props: { } 7 | }); 8 | 9 | export default app; -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import { svelte } from '@sveltejs/vite-plugin-svelte' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [ 7 | svelte() 8 | ] 9 | }) 10 | --------------------------------------------------------------------------------