Add GameProfile struct as aith result, serde now use stringly typed structures to parse version manifest json, add specific version json parsing
This commit is contained in:
parent
5ecbf08c0a
commit
4d2ed01cc0
@ -6,7 +6,7 @@ authors = ["you"]
|
||||
license = ""
|
||||
repository = ""
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
rust-version = "1.64"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -2,7 +2,7 @@ use std::{fmt, net::TcpListener, sync::Arc};
|
||||
|
||||
use rand::{thread_rng, Rng};
|
||||
use reqwest::{header::{CONTENT_TYPE, CONNECTION, ACCEPT, AUTHORIZATION}, Client};
|
||||
use serde_json::{Value, json};
|
||||
use serde_json::{Value, json, Map};
|
||||
use tokio::{sync::mpsc, join};
|
||||
use urlencoding::encode;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -50,6 +50,14 @@ pub struct ReceivedCode {
|
||||
pub state: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct GameProfile {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub skins: Vec<Value>,
|
||||
pub capes: Vec<Value>,
|
||||
}
|
||||
|
||||
pub struct Authentification;
|
||||
|
||||
impl Authentification {
|
||||
@ -233,7 +241,7 @@ impl Authentification {
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_minecraft_profile(mc_token: &String, reqwest_client: &Client) -> Result<(String, String)> {
|
||||
async fn fetch_minecraft_profile(mc_token: &String, reqwest_client: &Client) -> Result<GameProfile> {
|
||||
let received: Value = reqwest_client
|
||||
.get("https://api.minecraftservices.com/minecraft/profile")
|
||||
.header(AUTHORIZATION, format!("Bearer {}", mc_token))
|
||||
@ -245,11 +253,15 @@ impl Authentification {
|
||||
if let Some(val) = received.get("error") {
|
||||
bail!(String::from(val.as_str().unwrap()))
|
||||
} else {
|
||||
Ok((String::from(received["id"].as_str().unwrap()), String::from(received["name"].as_str().unwrap())))
|
||||
let received: GameProfile = match serde_json::from_value(received) {
|
||||
Ok(gp) => gp,
|
||||
Err(err) => bail!(err),
|
||||
};
|
||||
Ok(received)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn login(prompt: Prompt, app: tauri::AppHandle) -> Result<(String, String)> {
|
||||
pub async fn login(prompt: Prompt, app: tauri::AppHandle) -> Result<GameProfile> {
|
||||
let reqwest_client = Client::new();
|
||||
let oauth_token = Self::fetch_oauth2_token(prompt, app).await?;
|
||||
let access_refresh_token = Self::fetch_token(oauth_token.0, oauth_token.1, &reqwest_client).await?;
|
||||
|
@ -1,13 +1,31 @@
|
||||
use std::fmt::Display;
|
||||
use std::{fmt::Display, path::{self, Path}};
|
||||
|
||||
use anyhow::{Result, bail, anyhow};
|
||||
use anyhow::{Result, bail};
|
||||
use reqwest::Client;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde_json::{Value, Map};
|
||||
use tokio::fs;
|
||||
|
||||
use crate::authentification::GameProfile;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct VersionManifestV2 {
|
||||
latest: Value,
|
||||
versions: Vec<Version>
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct Version {
|
||||
id: String,
|
||||
#[serde(rename(serialize = "type", deserialize = "type"))]
|
||||
v_type: VersionType,
|
||||
url: String,
|
||||
sha1: String
|
||||
}
|
||||
|
||||
|
||||
|
||||
async fn get_version_manifest(reqwest: &Client) -> Result<Value> {
|
||||
let received: Value = reqwest
|
||||
async fn get_version_manifest(reqwest: &Client) -> Result<VersionManifestV2> {
|
||||
let received: VersionManifestV2 = reqwest
|
||||
.get("https://piston-meta.mojang.com/mc/game/version_manifest_v2.json")
|
||||
.send()
|
||||
.await?
|
||||
@ -16,40 +34,145 @@ async fn get_version_manifest(reqwest: &Client) -> Result<Value> {
|
||||
Ok(received)
|
||||
}
|
||||
|
||||
fn get_version_from_manifest<'a>(manifest: &'a Value, game_version: String, version_type: &VersionType) -> Result<&'a Map<String, Value>> {
|
||||
let versions = manifest.get("versions").ok_or(anyhow!("Manifest format is invalid"))?;
|
||||
let arr = versions.as_array().ok_or(anyhow!("Manifest format is invalid"))?;
|
||||
for i in arr.iter().enumerate() {
|
||||
let map = i.1.as_object().ok_or(anyhow!("Manifest format is invalid"))?;
|
||||
let id = map.get("id").ok_or(anyhow!("Manifest format is invalid, cannot find version id"))?;
|
||||
let id = id.as_str().ok_or(anyhow!("Manifest format is invalid, id is not a str"))?;
|
||||
let v_type = map.get("type").ok_or(anyhow!("Manifest format is invalid, cannot find version type"))?;
|
||||
let v_type = v_type.as_str().ok_or(anyhow!("Manifest format is invalid, type is not a str"))?;
|
||||
if id == game_version && v_type.try_into() == Ok(version_type) {
|
||||
return Ok(map);
|
||||
fn get_version_from_manifest<'a>(manifest: &'a VersionManifestV2, game_version: String, version_type: &VersionType) -> Result<&'a Version> {
|
||||
for i in manifest.versions.iter().enumerate() {
|
||||
let id = i.1.id.clone();
|
||||
let v_type = i.1.v_type;
|
||||
if id == game_version && &v_type == version_type {
|
||||
return Ok(i.1);
|
||||
}
|
||||
}
|
||||
bail!("Version not Found")
|
||||
}
|
||||
|
||||
fn get_version_detail(reqwest: &Client, version : &Map<String, Value>) -> Result<()> {
|
||||
bail!("Not implemented yet")
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct VersionDetail {
|
||||
arguments: Map<String, Value>,
|
||||
#[serde(rename(serialize = "assetIndex", deserialize = "assetIndex"))]
|
||||
asset_index: Map<String, Value>,
|
||||
assets: String,
|
||||
downloads: Map<String, Value>,
|
||||
id: String,
|
||||
#[serde(rename(serialize = "javaVersion", deserialize = "javaVersion"))]
|
||||
java_version: Map<String, Value>,
|
||||
libraries: Vec<Library>,
|
||||
logging: Map<String, Value>,
|
||||
#[serde(rename(serialize = "mainClass", deserialize = "mainClass"))]
|
||||
main_class: String,
|
||||
#[serde(rename(serialize = "type", deserialize = "type"))]
|
||||
v_type: VersionType
|
||||
}
|
||||
|
||||
pub async fn download_assets(game_version: String, version_type: &VersionType) -> Result<()> {
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Library {
|
||||
downloads: LibraryDownload,
|
||||
name: String,
|
||||
rules: Vec<LibraryRule>
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct LibraryRule {
|
||||
action: String,
|
||||
os: LibraryOSRule
|
||||
}
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct LibraryOSRule {
|
||||
name: OSName,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
enum OSName {
|
||||
#[serde(rename(serialize = "osx", deserialize = "osx"))]
|
||||
MacOsX,
|
||||
#[serde(rename(serialize = "linux", deserialize = "linux"))]
|
||||
Linux,
|
||||
#[serde(rename(serialize = "windows", deserialize = "windows"))]
|
||||
Windows
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct LibraryDownload {
|
||||
artifact: LibraryArtifact
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct LibraryArtifact {
|
||||
path: String,
|
||||
sha1: String,
|
||||
size: i64,
|
||||
url: String,
|
||||
}
|
||||
|
||||
async fn get_version_detail(reqwest: &Client, version : &Version) -> Result<VersionDetail> {
|
||||
let received: VersionDetail = reqwest
|
||||
.get(version.url.clone())
|
||||
.send()
|
||||
.await?
|
||||
.json()
|
||||
.await?;
|
||||
Ok(received)
|
||||
}
|
||||
pub struct ClientOptions<'a> {
|
||||
authorization: GameProfile,
|
||||
root_path: &'a Path,
|
||||
javaPath: String,
|
||||
version_number: String,
|
||||
version_type: VersionType,
|
||||
// version_custom: String, // for a next update
|
||||
memory_min: String,
|
||||
memory_max: String,
|
||||
}
|
||||
|
||||
pub struct MinecraftClient<'a> {
|
||||
opts: ClientOptions<'a>,
|
||||
details: VersionDetail,
|
||||
reqwest_client: Client,
|
||||
|
||||
}
|
||||
|
||||
impl<'a> MinecraftClient<'_> {
|
||||
pub async fn new(opts: ClientOptions<'a>) -> Result<MinecraftClient<'a>> {
|
||||
let reqwest_client = Client::new();
|
||||
let manifest = get_version_manifest(&reqwest_client).await?;
|
||||
let version = get_version_from_manifest(&manifest, game_version, version_type)?;
|
||||
Ok(())
|
||||
let details = Self::load_manifest(&reqwest_client, &opts).await?;
|
||||
Ok(MinecraftClient {
|
||||
opts,
|
||||
reqwest_client,
|
||||
details,
|
||||
})
|
||||
}
|
||||
|
||||
async fn load_manifest(reqwest_client: &Client, opts: &ClientOptions<'a>) -> Result<VersionDetail> {
|
||||
let manifest = get_version_manifest(&reqwest_client).await?;
|
||||
let version = get_version_from_manifest(&manifest, opts.version_number.clone(), &opts.version_type)?;
|
||||
let details = get_version_detail(&reqwest_client, version).await?;
|
||||
Ok(details)
|
||||
}
|
||||
|
||||
pub async fn download_assets(&self) -> Result<()> {
|
||||
// create root folder if it doesn't exist
|
||||
fs::create_dir_all(self.opts.root_path).await?;
|
||||
fs::create_dir(self.opts.root_path.join("librairies")).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
/// Filter non necessary librairies for the current OS
|
||||
fn filter_non_necessary_librairies(&self) -> Result<()> {
|
||||
bail!("Not implemented yet")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
|
||||
|
||||
#[derive(PartialEq, Serialize, Deserialize, Debug, Clone, Copy)]
|
||||
pub enum VersionType {
|
||||
#[serde(alias = "release")]
|
||||
Release,
|
||||
#[serde(alias = "snapshot")]
|
||||
Snapshot,
|
||||
#[serde(alias = "old_alpha")]
|
||||
OldAlpha,
|
||||
#[serde(alias = "old_beta")]
|
||||
OldBeta,
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ fn greet(name: &str) -> String {
|
||||
async fn login(app: tauri::AppHandle, _window: tauri::Window) -> Result<String, String> {
|
||||
let result = Authentification::login(Prompt::SelectAccount, app).await;
|
||||
match result {
|
||||
Ok(val) => Ok(format!("Hello {}", val.1)),
|
||||
Ok(val) => Ok(format!("Hello {}", val.name)),
|
||||
Err(err) => Err(err.to_string())
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user