diff --git a/Cargo.lock b/Cargo.lock index f4af7174..943218c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1055,15 +1055,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "indoc" -version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" -dependencies = [ - "rustversion", -] - [[package]] name = "inout" version = "0.1.4" @@ -1272,15 +1263,6 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - [[package]] name = "mime" version = "0.3.17" @@ -1726,7 +1708,7 @@ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portablemc" -version = "5.0.3" +version = "5.0.4" dependencies = [ "chrono", "dirs", @@ -1756,7 +1738,7 @@ dependencies = [ [[package]] name = "portablemc-cli" -version = "5.0.3" +version = "5.0.4" dependencies = [ "chrono", "clap", @@ -1777,7 +1759,7 @@ dependencies = [ [[package]] name = "portablemc-ffi" -version = "5.0.3" +version = "5.0.4" dependencies = [ "portablemc", "serde", @@ -1788,7 +1770,7 @@ dependencies = [ [[package]] name = "portablemc-py" -version = "5.0.3" +version = "5.0.4" dependencies = [ "portablemc", "pyo3", @@ -1846,37 +1828,32 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.23.5" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7778bffd85cf38175ac1f545509665d0b9b92a198ca7941f131f85f7a4f9a872" +checksum = "91fd8e38a3b50ed1167fb981cd6fd60147e091784c427b8f7183a7ee32c31c12" dependencies = [ - "cfg-if", - "indoc", "libc", - "memoffset", "once_cell", "portable-atomic", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", - "unindent", ] [[package]] name = "pyo3-build-config" -version = "0.23.5" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f6cbe86ef3bf18998d9df6e0f3fc1050a8c5efa409bf712e661a4366e010fb" +checksum = "e368e7ddfdeb98c9bca7f8383be1648fd84ab466bf2bc015e94008db6d35611e" dependencies = [ - "once_cell", "target-lexicon", ] [[package]] name = "pyo3-ffi" -version = "0.23.5" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f1b4c431c0bb1c8fb0a338709859eed0d030ff6daa34368d3b152a63dfdd8d" +checksum = "7f29e10af80b1f7ccaf7f69eace800a03ecd13e883acfacc1e5d0988605f651e" dependencies = [ "libc", "pyo3-build-config", @@ -1884,9 +1861,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.23.5" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc2201328f63c4710f68abdf653c89d8dbc2858b88c5d88b0ff38a75288a9da" +checksum = "df6e520eff47c45997d2fc7dd8214b25dd1310918bbb2642156ef66a67f29813" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -1896,9 +1873,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.23.5" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca6726ad0f3da9c9de093d6f116a93c1a38e417ed73bf138472cf4064f72028" +checksum = "c4cdc218d835738f81c2338f822078af45b4afdf8b2e33cbb5916f108b813acb" dependencies = [ "heck", "proc-macro2", @@ -2453,9 +2430,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.16" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" [[package]] name = "tempfile" @@ -2689,12 +2666,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "unindent" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" - [[package]] name = "untrusted" version = "0.9.0" @@ -3219,7 +3190,7 @@ checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" [[package]] name = "xtask" -version = "5.0.3" +version = "5.0.4" dependencies = [ "flate2", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index a5255d59..0fe80fc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ resolver = "2" [workspace.package] edition = "2024" -version = "5.0.3" +version = "5.0.4" authors = ["Théo Rozier "] homepage = "https://github.com/theorzr/portablemc" repository = "https://github.com/theorzr/portablemc" @@ -14,7 +14,7 @@ readme = "README.md" rust-version = "1.88.0" [workspace.dependencies] -portablemc = { path = "portablemc", version = "=5.0.3" } +portablemc = { path = "portablemc", version = "=5.0.4" } # Utils thiserror = "2.0.3" diff --git a/README.md b/README.md index a3a004d6..cfd0c6cf 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Portable Minecraft Launcher +# PortableMC Cross platform command line utility for launching Minecraft quickly and reliably with included support for Mojang versions and popular mod loaders. It is also available as a Rust crate for developers ~~and bindings for C and Python~~ (yet to come). @@ -10,8 +10,10 @@ a Rust crate for developers ~~and bindings for C and Python~~ (yet to come). - [Installation](#installation) - [Binaries](#binaries) - [Cargo](#cargo) - - [Linux third-party packages](#linux-third-party-packages) + - [Linux packages](#linux-packages) - [Arch Linux](#arch-linux) + - [NixOS](#nixos) +- [Usage](#usage) - [Contribute](#contribute) - [Repositories](#repositories) - [Contributors](#contributors) @@ -37,10 +39,10 @@ a Rust crate for developers ~~and bindings for C and Python~~ (yet to come). ### Binaries -![GitHub Release](https://img.shields.io/github/v/release/mindstorm38/portablemc) +![GitHub Release](https://img.shields.io/github/v/release/theorzr/portablemc) You can directly download and run the portable binaries that are pre-built and available -as assets on [release pages](https://github.com/mindstorm38/portablemc/releases). +as assets on [release pages](https://github.com/theorzr/portablemc/releases). These binaries have been compiled using open source tooling available in this repository. You can ensure that these binaries have been compiled by the official PortableMC project @@ -49,7 +51,7 @@ PortableMC project is: `f659b0f0b84a26cac635d72948caee8dc3456b2f` You can download the full PGP certificate online: - [Ubuntu Keyserver](https://keyserver.ubuntu.com/pks/lookup?search=F659+B0F0+B84A+26CA+C635+D729+48CA+EE8D+C345+6B2F&fingerprint=on&op=index) -- [Maintainer server](https://www.theorozier.fr/assets/pgp/portablemc.asc) +- [Maintainer server](https://theorozier.fr/assets/pgp/portablemc.asc.html) ### Cargo @@ -71,10 +73,10 @@ launcher, it is also available on [crates.io](https://crates.io/crates/portablem cargo add portablemc ``` -### Linux third-party packages +### Linux packages We try to deploy the package to different Linux packaging repositories, some are managed -by maintainers of the project and some by external maintainers. +by maintainers of the project (first-party) and some by external maintainers (third-party). #### Arch Linux @@ -87,30 +89,76 @@ Arch Linux packages are maintained by PortableMC team. Prebuilt binaries requires you to install the PGP certificate, as described [above](#binaries). +#### NixOS + +Nix package is maintained by @TomaSajt, at [`nixpkgs/portablemc`](https://github.com/NixOS/nixpkgs/blob/master/pkgs/by-name/po/portablemc/package.nix). + +## Usage + +This section shows example usage to get started with PortableMC. + +```shell +# Start the latest Mojang release, with a random username and default options... +portablemc start + +# Start a specific version, let's say 1.16.5... +portablemc start 1.16.5 +# You can list the Mojang versions... +portablemc search +# Search on the release versions, and limit to 10 entries... +portablemc search --channel release -l10 + +# Choose your username in offline mode... +portablemc start -u MyUsername + +# Authenticate into your Minecraft account... +portablemc auth login +# List your authenticated accounts... +portablemc auth list +# Start the game with your authenticated account... +portablemc start -u -a +``` + ## Contribute ### Repositories -The source code is currently tracked using Git and hosted [on GitHub](https://github.com/mindstorm38/portablemc). +The source code is currently tracked using Git and hosted [on GitHub](https://github.com/theorzr/portablemc). -We also have an official team workspace [on Codeberg.org](https://codeberg.org/portablemc) where we host a mirror -of this repository and the official third-party packaging sources. +We also have an official team workspace [on Codeberg.org](https://codeberg.org/portablemc) +where we host a mirror of this repository and the first-party packaging sources. ### Releasing Releasing process is mostly managed by GitHub actions, it reacts to new tags being -pushed to the repository by a repository admin. This tag should be named `v` where -`` is the same as the one in `Cargo.toml`, if not matching, the actions will fail. +pushed to the repository by a repository admin. This section is about how to prepare +the repository for that releasing process. + +To start with, every new release, which can be either a patch, minor or major release, +should have an associated branch that will be merged when releasing is done. It should +be named `v` and created from `main`. When you have merged everything into that +branch and you are ready for doing the release, you should ensure that the project's +version has been updated in `Cargo.toml` to match the branch and the tag you are about +to create, and then you can create and push a tag named `v`, the GitHub pipeline +will trigger automatically. + +If it make sense to have release candidates or pre-releases, this should be handled on +the same branch as the real release: you should append `-rc.` or `-pre.` to the +final release number. -Once completed, a draft release note is attached to that new tag under [releases](https://github.com/theorzr/portablemc/releases), -you should complete it with the actual changelog, and then publish the release note. -Note that the release artifacts are automatically uploaded and signed by the actions. +Once the pipeline is done, a draft release note is attached to that new tag under +[releases](https://github.com/theorzr/portablemc/releases), you should complete it with +the actual changelog, and then publish the release note manually. Note that the release +artifacts are automatically uploaded and signed by the actions. + +When this is all done, you can now merge and delete the version branch. Then, you should manage the releasing of official third-party packaging, such as [portablemc-arch](https://codeberg.org/portablemc/portablemc-arch) and [portablemc-bin-arch](https://codeberg.org/portablemc/portablemc-bin-arch). ### Contributors + This launcher would not be as functional without the contributors, and in particular the following for their bug reports, suggestions and pull requests to make the launcher better: @@ -125,10 +173,11 @@ There must be a lot of hidden issues, if you want to contribute you just have to and test the launcher, and report every issue you encounter, do not hesitate! ### Sponsors + I'm currently working on my open-source projects on my free time. So sponsorship is an extra income that allows me to spend more time on the project! This can also help me on other open-source projects. You can sponsor this project by donating either on -[GitHub Sponsors](https://github.com/sponsors/mindstorm38) or +[GitHub Sponsors](https://github.com/sponsors/theorzr) or [Ko-fi](https://ko-fi.com/theorozier). I've always been passionate about open-source programming and the relative success of PortableMC have been a first surprise to me, but the fact that people are now considering to support me financially is even more diff --git a/portablemc-cli/src/cmd/mod.rs b/portablemc-cli/src/cmd/mod.rs index 7262ce64..8632f4de 100644 --- a/portablemc-cli/src/cmd/mod.rs +++ b/portablemc-cli/src/cmd/mod.rs @@ -8,7 +8,8 @@ mod r#gen; use std::process::{self, ExitCode}; use std::path::{Path, PathBuf}; use std::time::Instant; -use std::io; +use std::{env, io}; +use std::fs; use portablemc::{base, download, moj, fabric, forge, msa}; @@ -648,8 +649,14 @@ pub fn log_base_error(cli: &mut Cli, error: &base::Error) { .error(format_args!("Library {gav} not found and no download information is available")); } Error::JvmNotFound { major_version } => { + let os_id = os_id(); let mut log = out.log("error_jvm_not_found"); log.error(format_args!("No compatible JVM found for the game version, which requires major version {major_version}")); + if os_id.iter().any(|s| s == "debian") { + log.additional(format_args!("It appears that you run a Debian distribution, you could try 'apt install openjdk-{major_version}-jre'")); + } else if os_id.iter().any(|s| s == "arch") { + log.additional(format_args!("It appears that you run a Arch distribution, you could try 'pacman -Sy jre{major_version}-openjdk'")); + } log.additional("You can enable verbose mode to learn more about potential JVM rejections"); if *major_version <= 8 { log.additional("Note that JVM version 8 and prior versions are not compatible with other versions"); @@ -1090,3 +1097,28 @@ fn forge_id_name(loader: forge::Loader) -> (&'static str, &'static str) { forge::Loader::NeoForge => ("neoforge", "NeoForge"), } } + +/// Internal function to get the current operating system distribution ID. +/// +/// See: https://www.freedesktop.org/software/systemd/man/latest/os-release.html +fn os_id() -> Vec { + + if env::consts::FAMILY == "unix" + && let Ok(release) = fs::read_to_string("/etc/os-release") { + let mut ret = Vec::new(); + for line in release.lines() { + let Some((k, v)) = line.split_once('=') else { continue }; + match k { + "ID" => ret.insert(0, v.to_string()), + "ID_LIKE" => ret.extend(v.split(' ').map(str::to_string)), + _ => continue, + } + } + if !ret.is_empty() { + return ret; + } + } + + vec![env::consts::OS.to_string()] + +} diff --git a/portablemc-cli/src/cmd/search.rs b/portablemc-cli/src/cmd/search.rs index 50066efc..cec26cb5 100644 --- a/portablemc-cli/src/cmd/search.rs +++ b/portablemc-cli/src/cmd/search.rs @@ -70,7 +70,12 @@ fn search_mojang(cli: &mut Cli, args: &SearchArgs) -> ExitCode { }); // Finally displaying version(s). - for version in manifest.iter().take(args.limit) { + let mut remaining = args.limit; + for version in manifest.iter() { + + if remaining == 0 { + break; + } if let Some(only_name) = only_name { if version.name() != only_name { @@ -92,6 +97,8 @@ fn search_mojang(cli: &mut Cli, args: &SearchArgs) -> ExitCode { } } + + remaining -= 1; let mut row = table.row(); row.cell(version.name()); @@ -157,7 +164,12 @@ fn search_local(cli: &mut Cli, args: &SearchArgs) -> ExitCode { table.sep(); - for entry in reader.take(args.limit) { + let mut remaining = args.limit; + for entry in reader { + + if remaining == 0 { + break; + } let Ok(entry) = entry else { continue }; let Ok(entry_type) = entry.file_type() else { continue }; @@ -167,6 +179,8 @@ fn search_local(cli: &mut Cli, args: &SearchArgs) -> ExitCode { let Some(version_id) = version_dir.file_name().unwrap().to_str() else { continue }; let version_id = version_id.to_string(); + remaining -= 1; + version_dir.push(&version_id); version_dir.as_mut_os_string().push(".json"); @@ -216,8 +230,13 @@ fn search_fabric(cli: &mut Cli, args: &SearchArgs, loader: fabric::Loader, game: table.sep(); - for version in versions.iter().take(args.limit) { + let mut remaining = args.limit; + for version in versions.iter() { + if remaining == 0 { + break; + } + if !args.match_filter(version.name()) { continue; } @@ -226,6 +245,8 @@ fn search_fabric(cli: &mut Cli, args: &SearchArgs, loader: fabric::Loader, game: continue; } + remaining -= 1; + let mut row = table.row(); row.cell(version.name()); row.cell(if version.is_stable() { "stable" } else { "unstable" }) @@ -253,7 +274,12 @@ fn search_fabric(cli: &mut Cli, args: &SearchArgs, loader: fabric::Loader, game: table.sep(); - for version in versions.iter().take(args.limit) { + let mut remaining = args.limit; + for version in versions.iter() { + + if remaining == 0 { + break; + } if !args.match_filter(version.name()) { continue; @@ -263,6 +289,8 @@ fn search_fabric(cli: &mut Cli, args: &SearchArgs, loader: fabric::Loader, game: continue; } + remaining -= 1; + let mut row = table.row(); row.cell(version.name()); row.cell(if version.is_stable() { "stable" } else { "unstable" }) @@ -304,8 +332,13 @@ fn search_forge(cli: &mut Cli, args: &SearchArgs, loader: forge::Loader) -> Exit // Forge .xml repositories are not sorted at all and therefore we have to sort them // here for the sorting to make sense to the user. let mut versions = Vec::new(); - for version in repo.iter().take(args.limit) { + let mut remaining = args.limit; + for version in repo.iter() { + if remaining == 0 { + break; + } + if !args.match_filter(version.name()) { continue; } @@ -318,6 +351,8 @@ fn search_forge(cli: &mut Cli, args: &SearchArgs, loader: forge::Loader) -> Exit continue; } + remaining -= 1; + versions.push(version); } diff --git a/portablemc-cli/src/cmd/start.rs b/portablemc-cli/src/cmd/start.rs index b805a03d..bad10628 100644 --- a/portablemc-cli/src/cmd/start.rs +++ b/portablemc-cli/src/cmd/start.rs @@ -389,7 +389,8 @@ fn run_command(cli: &mut Cli, mut command: Command) -> io::Result<()> { cli.out.log("launched") .arg(child.id()) - .success("Launched"); + .success("Launched") + .info(format_args!("Process: {}", child.id())); // Take the stdout pipe and put the child in the shared location, only then we // release the guard so any handled Ctrl-C will terminate it. diff --git a/portablemc-cli/src/parse.rs b/portablemc-cli/src/parse.rs index beabe650..034f05b5 100644 --- a/portablemc-cli/src/parse.rs +++ b/portablemc-cli/src/parse.rs @@ -291,7 +291,7 @@ pub struct StartArgs { /// where the game will check for natives to load. The main use case is for including /// shared objects (.so, .dll, .dylib), in case of versioned .so files like we can /// see on UNIX systems, the version is discarded when linked or copied to the bin - /// directory (/usr/lib/foo.so.1.22.2 -> foo.so). + /// directory (/usr/lib/foo.so.1.22.2 copied as foo.so). /// /// Read the help message of '--exclude-lib' for a typical use case. /// @@ -304,11 +304,11 @@ pub struct StartArgs { /// This argument can be specified multiple times. #[arg(long, value_name = "PATH", display_order = ORDER_LIB)] pub include_class: Vec, - /// The path to the JVM executable, 'java' (or 'javaw.exe' on Windows). + /// The path to the JVM executable, 'java' (or 'javaw.exe' on Windows) use to launch + /// the game. /// - /// This is used to launch the game, it has a special use-case with Forge and NeoForge - /// loader versions where that JVM executable is also used to run the installer - /// processors. + /// Note that with Forge and NeoForge mod loaders, that JVM executable is also used + /// to run the installer processors. /// /// Note that when this argument is specified, you cannot specify the '--jvm-policy' /// argument. @@ -322,9 +322,9 @@ pub struct StartArgs { /// You can specify multiple arguments after the '--jvm-arg' option, using commas ',', /// for example 'start --jvm-arg=-Xms256m,-Xmx2048m'. /// - /// Most of the time you should prefer using the '--jvm-arg=' form because JVM - /// arguments also starts with a dash, which would be ambiguous if using - /// '--jvm-arg -X...' (NOT WORKING) for example. + /// Most of the time you should prefer using the '--jvm-arg=' (with an EQUAL sign) + /// form because JVM arguments also starts with a dash, which would be ambiguous if + /// using '--jvm-arg -X...' (NOT WORKING) for example. #[arg(long, value_name = "ARG", value_delimiter(','), display_order = ORDER_JVM)] pub jvm_arg: Vec, /// Automatically join the given singleplayer world after game has been launched. @@ -520,9 +520,9 @@ impl FromStr for StartVersion { pub enum StartJvmPolicy { /// The installer will try to find a suitable JVM executable in the path, searching /// a `java` (or `javaw.exe` on Windows) executable. On operating systems where it's - /// supported, this will also check for known directories (on Arch for example). + /// supported, this will also check for known directories and registries. /// If the version needs a specific JVM major version, each candidate executable is - /// checked and a warning is triggered to notify that the version is not suited. + /// checked and a warning is triggered to notify that the version is not suitable. /// The install fails if none of those versions is valid. System, /// The installer will try to find a suitable JVM to install from Mojang-provided @@ -788,10 +788,6 @@ pub enum SearchLatestChannel { /// Manage the authentication sessions. /// -/// By default, this command will start a new authentication flow with the Microsoft -/// authentication service, when completed this will add the newly authenticated session -/// to the authentication database (specified with '--msa-db-file' argument). -/// /// If this command fails to load and/or store the database, its exit code is 1 (failure). #[derive(Debug, Args)] pub struct AuthArgs { diff --git a/portablemc-py/Cargo.toml b/portablemc-py/Cargo.toml index 36125fd9..6cad9c09 100644 --- a/portablemc-py/Cargo.toml +++ b/portablemc-py/Cargo.toml @@ -16,7 +16,7 @@ portablemc.workspace = true uuid.workspace = true -pyo3 = { version = "0.23.4", features = ["extension-module"] } +pyo3 = { version = "0.28.3", features = ["extension-module"] } [lib] name = "portablemc_py" diff --git a/portablemc/src/base/mod.rs b/portablemc/src/base/mod.rs index 215671a0..1e43ea4e 100644 --- a/portablemc/src/base/mod.rs +++ b/portablemc/src/base/mod.rs @@ -1169,6 +1169,7 @@ impl Installer { 16 => "java-runtime-alpha", 17 => "java-runtime-gamma", 21 => "java-runtime-delta", + 25 => "java-runtime-epsilon", _ => return None })); diff --git a/portablemc/src/moj/mod.rs b/portablemc/src/moj/mod.rs index 92e9bf8d..fb932cca 100644 --- a/portablemc/src/moj/mod.rs +++ b/portablemc/src/moj/mod.rs @@ -1112,12 +1112,29 @@ impl InternalHandler<'_> { fn apply_fix_lwjgl(&mut self, libraries: &mut Vec, version: &str) -> Result<()> { - if version != "3.2.3" && !version.starts_with("3.3.") { + let Some(("", minor_patch)) = version.split_once("3.") else { return Err(Error::LwjglFixNotFound { version: version.to_string(), }); + }; + + if minor_patch != "2.3" { + + let (minor, _) = minor_patch.split_once('.').unwrap_or((minor_patch, "")); + let Ok(minor) = minor.parse::() else { + return Err(Error::LwjglFixNotFound { + version: version.to_string(), + }); + }; + + if minor < 3 { + return Err(Error::LwjglFixNotFound { + version: version.to_string(), + }); + } + } - + let classifier = match (env::consts::OS, env::consts::ARCH) { ("windows", "x86") => "natives-windows-x86", ("windows", "x86_64") => "natives-windows",