Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 12 additions & 41 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 37 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -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).
Expand All @@ -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)
Expand Down Expand Up @@ -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

Expand All @@ -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

Expand All @@ -87,6 +89,36 @@ 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 <your username> -a
```

## Contribute

### Repositories
Expand Down
34 changes: 33 additions & 1 deletion portablemc-cli/src/cmd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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<String> {

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()]

}
45 changes: 40 additions & 5 deletions portablemc-cli/src/cmd/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -92,6 +97,8 @@ fn search_mojang(cli: &mut Cli, args: &SearchArgs) -> ExitCode {
}

}

remaining -= 1;

let mut row = table.row();
row.cell(version.name());
Expand Down Expand Up @@ -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 };
Expand All @@ -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");

Expand Down Expand Up @@ -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;
}
Expand All @@ -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" })
Expand Down Expand Up @@ -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;
Expand All @@ -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" })
Expand Down Expand Up @@ -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;
}
Expand All @@ -318,6 +351,8 @@ fn search_forge(cli: &mut Cli, args: &SearchArgs, loader: forge::Loader) -> Exit
continue;
}

remaining -= 1;

versions.push(version);

}
Expand Down
3 changes: 2 additions & 1 deletion portablemc-cli/src/cmd/start.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading
Loading