Downloads a file and writes it to disk
This commit is contained in:
parent
ba61b4717e
commit
2d99342b68
8288
.minecraft/downloads/manifest.json
Normal file
8288
.minecraft/downloads/manifest.json
Normal file
File diff suppressed because it is too large
Load Diff
1827
Cargo.lock
generated
Normal file
1827
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
16
Cargo.toml
16
Cargo.toml
@ -1,6 +1,16 @@
|
||||
[package]
|
||||
name = "mc-launcher-rs"
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = ["crates/*"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
[workspace.dependencies]
|
||||
anyhow = "1.0.104"
|
||||
const_format = "0.2.36"
|
||||
reqwest = "0.13.5"
|
||||
serde = { version = "1.0.229", features = ["derive"] }
|
||||
serde_json = "1.0.154"
|
||||
tokio = { version = "1.54.0", features = ["full"] }
|
||||
thiserror = "2.0.21"
|
||||
|
||||
43
README.md
Normal file
43
README.md
Normal file
@ -0,0 +1,43 @@
|
||||
# mc-launcher-rs
|
||||
|
||||
A simple minecraft launcher, in Rust
|
||||
|
||||
## Goal
|
||||
The goal of this project is for me to learn a little bit of Rust, and experiment with the ecosystem.
|
||||
The current scope of the project is just downloading minecraft and launching it.
|
||||
|
||||
Maybe modloader after all is stable-ish
|
||||
|
||||
## Stack
|
||||
// Todo, link
|
||||
| Usage | Choice |
|
||||
| -------- | ----------- |
|
||||
| UI | egui |
|
||||
| Async | tokio |
|
||||
| HTTP | reqwest |
|
||||
| JSON | serde |
|
||||
| Hashing | sha1 |
|
||||
| Archives | zip |
|
||||
| Folders | directories |
|
||||
| Errors | anyhow |
|
||||
| Logs | tracing |
|
||||
|
||||
## Roadmap
|
||||
Do the download logic, then the UI.
|
||||
|
||||
Download:
|
||||
[x] Read the manifest
|
||||
[x] Manifest -> JSON
|
||||
[] Interpret the data
|
||||
[] Download from the JSON -> HTTP/Tokio
|
||||
[] Go from sequential to parallel
|
||||
|
||||
## Structure
|
||||
`core` -> all the launcher stuff, version manifest, JSON, download, hashes, launch args...
|
||||
`cli` -> a small wrapper that calls the core and display text
|
||||
`gui` -> another small wrapper that calls the core and displays it with `egui`
|
||||
|
||||
|
||||
## Links
|
||||
https://piston-meta.mojang.com/mc/game/version_manifest_v2.json
|
||||
This one is the main manifest
|
||||
14
crates/cli/Cargo.toml
Normal file
14
crates/cli/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "launcher-cli"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
launcher-core = { version = "0.1.0", path = "../core" }
|
||||
tokio = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
reqwest = { workspace = true }
|
||||
const_format = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
12
crates/cli/src/main.rs
Normal file
12
crates/cli/src/main.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use anyhow::Result;
|
||||
use launcher_core::manifest::{get_manifest, init_network, parse_manifest, write_manifest};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
println!("Runtime test, writing the manifest");
|
||||
let client = init_network();
|
||||
let data = get_manifest(client.clone()).await?;
|
||||
write_manifest(&data).await?;
|
||||
parse_manifest(&data).await?;
|
||||
Ok(())
|
||||
}
|
||||
16
crates/core/Cargo.toml
Normal file
16
crates/core/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "launcher-core"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
tokio = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
reqwest = { workspace = true }
|
||||
const_format = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { workspace = true }
|
||||
13
crates/core/src/errors.rs
Normal file
13
crates/core/src/errors.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CoreError {
|
||||
#[error("Network error: {0}")]
|
||||
Network(#[from] reqwest::Error),
|
||||
|
||||
#[error("Disk error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
#[error("JSON error: {0}")]
|
||||
JSON(#[from] serde_json::Error),
|
||||
}
|
||||
12
crates/core/src/lib.rs
Normal file
12
crates/core/src/lib.rs
Normal file
@ -0,0 +1,12 @@
|
||||
pub mod errors;
|
||||
pub mod manifest;
|
||||
|
||||
use const_format::concatcp;
|
||||
// Constants
|
||||
// Paths
|
||||
pub const FILE_PATH: &str = ".minecraft"; //adjusted later
|
||||
pub const DOWNLOAD_PATH: &str = concatcp!(FILE_PATH, "/downloads");
|
||||
pub const MANIFEST_PATH: &str = concatcp!(DOWNLOAD_PATH, "/manifest.json");
|
||||
|
||||
// Urls
|
||||
pub const MANIFEST_URL: &str = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json";
|
||||
39
crates/core/src/manifest.rs
Normal file
39
crates/core/src/manifest.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use reqwest::Client;
|
||||
use serde_json::Value;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::{DOWNLOAD_PATH, MANIFEST_PATH, MANIFEST_URL, errors::CoreError};
|
||||
|
||||
pub fn init_network() -> Arc<Client> {
|
||||
let client = Arc::new(
|
||||
Client::builder()
|
||||
.timeout(Duration::from_secs(60))
|
||||
.build()
|
||||
.expect("Failed to create HTTP client"),
|
||||
);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
pub async fn get_manifest(client: Arc<Client>) -> Result<String, CoreError> {
|
||||
let body = client.get(MANIFEST_URL).send().await?.text().await?;
|
||||
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
pub async fn write_manifest(data: &str) -> Result<(), CoreError> {
|
||||
// Checks if the folder is there, else, create it
|
||||
tokio::fs::create_dir_all(DOWNLOAD_PATH).await?;
|
||||
tokio::fs::write(MANIFEST_PATH, data).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn parse_manifest(data: &str) -> Result<Value, CoreError> {
|
||||
// Reads from a string
|
||||
let v: Value = serde_json::from_str(data)?;
|
||||
|
||||
println!("Testing stuff.\nSo latest {}", v["latest"]["release"]);
|
||||
|
||||
Ok(v)
|
||||
}
|
||||
1
crates/core/tests/data/version_manifest_v2.json
Normal file
1
crates/core/tests/data/version_manifest_v2.json
Normal file
File diff suppressed because one or more lines are too long
11
crates/core/tests/tests.rs
Normal file
11
crates/core/tests/tests.rs
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
#[cfg(test)]
|
||||
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_write() {
|
||||
assert!()
|
||||
}
|
||||
}
|
||||
*/
|
||||
7
crates/gui/Cargo.toml
Normal file
7
crates/gui/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "launcher-gui"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
launcher-core = { version = "0.1.0", path = "../core" }
|
||||
57
flake.lock
generated
Normal file
57
flake.lock
generated
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1792506237,
|
||||
"narHash": "sha256-hVMDyAXU0kxRuG3GygbZkzf5x+aBNEC0NYEJ6KwHtD4=",
|
||||
"rev": "5adc50831c259f87faeb09ddf930d43597197473",
|
||||
"type": "tarball",
|
||||
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.11pre1097770.5adc50831c25/nixexprs.tar.zst"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs",
|
||||
"utils": "utils"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1791914995,
|
||||
"narHash": "sha256-Zf/xfQEAasnwnmEfHqW/4hjDE/s44yn2hD4RuVIZRjA=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b7a79d3fea1e214f6d668d521cecec0c01e26544",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "flake-utils",
|
||||
"type": "indirect"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user