feat: it launches!
This commit is contained in:
parent
f000ba9bdf
commit
b77f48ed90
12 changed files with 138 additions and 53 deletions
|
|
@ -16,6 +16,7 @@ pub struct Account {
|
|||
pub username: String,
|
||||
pub uuid: String,
|
||||
pub skins: Vec<AccountSkin>,
|
||||
pub access_token: String, // TODO deal with this properly
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ pub const NATIVE_ARCH: &str = "64";
|
|||
#[cfg(all(not(target_arch = "x86_64"), not(target_arch = "x86")))]
|
||||
pub const NATIVE_ARCH: &str = "64";
|
||||
|
||||
|
||||
// Minecraft library splitter
|
||||
#[cfg(target_os = "windows")]
|
||||
pub const LIBRARY_SPLITTER: &str = ";";
|
||||
|
|
|
|||
|
|
@ -1,9 +1,15 @@
|
|||
use std::{io::{stderr, stdout}, path::PathBuf};
|
||||
|
||||
use anyhow::anyhow;
|
||||
use async_trait::async_trait;
|
||||
use chrono::{DateTime, Local};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::process;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::{clients::ClientType, minecraft::MinecraftManifest};
|
||||
use crate::auth::Account;
|
||||
|
||||
use super::{clients::ClientType, minecraft::{MinecraftManifest, ModernArgumentsItemExt}};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Manifest {
|
||||
|
|
@ -23,22 +29,81 @@ pub struct Cluster {
|
|||
pub client: ClientType,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LaunchInfo {
|
||||
pub java: PathBuf,
|
||||
pub account: Account,
|
||||
pub mem_min: u32,
|
||||
pub mem_max: u32,
|
||||
pub setup: SetupInfo,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SetupInfo {
|
||||
pub version: String,
|
||||
pub libraries: String,
|
||||
pub game_dir: PathBuf,
|
||||
pub natives_dir: PathBuf,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait ClientTrait<'a>: Send + Sync {
|
||||
fn new(cluster: &'a Cluster, manifest: &'a Manifest) -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
fn new(cluster: &'a Cluster, manifest: &'a Manifest) -> Self where Self: Sized;
|
||||
|
||||
async fn launch(&self) -> crate::Result<()>;
|
||||
async fn setup(&self) -> crate::Result<()>;
|
||||
async fn launch(&self, info: LaunchInfo) -> crate::Result<()> {
|
||||
let manifest = &self.get_manifest().minecraft_manifest;
|
||||
|
||||
fn get_cluster(&self) -> &'a Cluster
|
||||
where
|
||||
Self: Sized;
|
||||
let args = get_arguments(manifest)?
|
||||
.replace("${auth_player_name}", &info.account.username)
|
||||
.replace("${version_name}", &info.setup.version)
|
||||
.replace("${game_directory}", info.setup.game_dir.to_str().unwrap())
|
||||
.replace("${assets_root}", format!("{}/assets", info.setup.game_dir.to_str().unwrap()).as_str())
|
||||
.replace("${assets_index_name}", &manifest.asset_index.id)
|
||||
.replace("${auth_uuid}", &info.account.uuid)
|
||||
.replace("${auth_access_token}", &info.account.access_token)
|
||||
.replace("${auth_session}", "0") // TODO: Figure out how to get this session ID.
|
||||
.replace("${user_properties}", "{}") // TODO: Figure out these properties.
|
||||
.replace("${user_type}", "1");
|
||||
|
||||
fn get_manifest(&self) -> &'a Manifest
|
||||
where
|
||||
Self: Sized;
|
||||
process::Command::new(&info.java).arg("-XX:-UseAdaptiveSizePolicy")
|
||||
.arg("-XX:-OmitStackTraceInFastThrow")
|
||||
.arg("-Dminecraft.launcher.brand=onelauncher")
|
||||
.arg("-Dminecraft.launcher.version=1")
|
||||
.arg(format!("-Djava.library.path={}", info.setup.natives_dir.to_str().unwrap()))
|
||||
.arg(format!("-Xms{}M", info.mem_min))
|
||||
.arg(format!("-Xmx{}M", info.mem_max))
|
||||
.arg("-cp")
|
||||
.arg(info.setup.libraries)
|
||||
.arg(manifest.main_class.clone())
|
||||
.args(args.split_whitespace())
|
||||
.stdout(stdout()) // TODO: Implement better logging
|
||||
.stderr(stderr())
|
||||
.spawn()?
|
||||
.wait()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn setup(&self) -> crate::Result<SetupInfo>;
|
||||
|
||||
fn get_cluster(&self) -> &'a Cluster;
|
||||
|
||||
fn get_manifest(&self) -> &'a Manifest;
|
||||
}
|
||||
|
||||
pub fn get_arguments(manifest: &MinecraftManifest) -> crate::Result<String> {
|
||||
// Modern
|
||||
if let Some(arguments) = &manifest.arguments {
|
||||
return Ok(arguments.game.build());
|
||||
}
|
||||
|
||||
// Legacy
|
||||
if let Some(arguments) = &manifest.minecraft_arguments {
|
||||
return Ok(arguments.clone());
|
||||
}
|
||||
|
||||
Err(anyhow!("No arguments found").into())
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ use chrono::Local;
|
|||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::utils::dirs;
|
||||
use crate::{auth::Account, utils::dirs};
|
||||
|
||||
use super::{client::{ClientTrait, Cluster, Manifest}, clients::{self, vanilla, ClientType}};
|
||||
use super::{client::{ClientTrait, Cluster, LaunchInfo, Manifest}, clients::{self, vanilla, ClientType}, java};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ClientManagerError {
|
||||
|
|
@ -77,8 +77,25 @@ impl ClientManager {
|
|||
uuid: Uuid,
|
||||
) -> crate::Result<()> {
|
||||
let client = self.get_impl_uuid(uuid)?;
|
||||
client.setup().await?;
|
||||
client.launch().await?;
|
||||
|
||||
let java = java::download_java(&dirs::java_dir()?, client.get_manifest().minecraft_manifest.java_version.major_version).await?;
|
||||
let setup = client.setup().await?;
|
||||
|
||||
// TODO: Make this configurable
|
||||
let info = LaunchInfo {
|
||||
java,
|
||||
setup,
|
||||
account: Account {
|
||||
username: "Player 0".to_string(),
|
||||
access_token: "0".to_string(),
|
||||
uuid: Uuid::new_v4().to_string(),
|
||||
skins: vec![]
|
||||
},
|
||||
mem_min: 1024,
|
||||
mem_max: 4096,
|
||||
};
|
||||
|
||||
client.launch(info).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,12 +14,6 @@ pub enum ClientType {
|
|||
Vanilla(VanillaClientProps),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LaunchInfo {
|
||||
// This is where things like account info, features etc will go
|
||||
pub java: PathBuf
|
||||
}
|
||||
|
||||
pub async fn get_versions(client: &ClientType, file: Option<&PathBuf>) -> crate::Result<Vec<MinecraftVersion>> {
|
||||
match client {
|
||||
ClientType::Vanilla(_) => vanilla::get_versions(file).await,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use crate::{
|
||||
constants::{self, MINECRAFT_VERSIONS_MANIFEST},
|
||||
create_game_client,
|
||||
game::{client::{ClientTrait, Cluster, Manifest}, minecraft::{Library, MinecraftManifest, MinecraftVersion, RuleListExt}},
|
||||
game::{client::{ClientTrait, Cluster, Manifest, SetupInfo}, minecraft::{Library, MinecraftManifest, MinecraftVersion, RuleListExt}},
|
||||
impl_game_client,
|
||||
utils::{dirs, http}
|
||||
utils::{dirs, file, http}
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use async_trait::async_trait;
|
||||
|
|
@ -27,16 +27,25 @@ impl<'a> ClientTrait<'a> for VanillaClient<'a> {
|
|||
VanillaClient { cluster, manifest }
|
||||
}
|
||||
|
||||
async fn launch(&self) -> crate::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn setup(&self) -> crate::Result<()> {
|
||||
async fn setup(&self) -> crate::Result<SetupInfo> {
|
||||
let manifest = &self.manifest.minecraft_manifest;
|
||||
|
||||
let client_path = install_game(manifest).await?;
|
||||
let libraries = install_libraries(manifest).await?;
|
||||
Ok(())
|
||||
let mut libraries = install_libraries(manifest).await?;
|
||||
|
||||
// Append client path to the libraries string at the end
|
||||
libraries.push_str(constants::LIBRARY_SPLITTER);
|
||||
libraries.push_str(client_path.to_str().unwrap());
|
||||
|
||||
let game_dir = dirs::game_dir()?.join(&self.cluster.id.to_string());
|
||||
fs::create_dir_all(&game_dir)?;
|
||||
|
||||
Ok(SetupInfo {
|
||||
version: manifest.version.clone(),
|
||||
libraries,
|
||||
natives_dir: dirs::natives_dir()?,
|
||||
game_dir,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -109,6 +118,7 @@ pub async fn install_natives(natives: Vec<&Library>) -> crate::Result<()> {
|
|||
|
||||
if !dest.exists() {
|
||||
http::download_file_sha1_check(url.as_str(), &dest, &artifact.sha1).await?;
|
||||
file::extract_zip(&dest, &dirs::natives_dir()?)?; // TODO: Handle this properly
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ async fn download(
|
|||
}
|
||||
|
||||
fn extract(archive: &PathBuf, dest: &PathBuf) -> crate::Result<()> {
|
||||
if let Err(err) = file::extract_archive(archive.as_path(), dest.as_path()) {
|
||||
if let Err(err) = file::extract_archive(archive, dest) {
|
||||
let _ = fs::remove_file(dest.as_path());
|
||||
return Err(ErrorKind::JavaError(JavaDownloadError::ExtractError(err.to_string())).into());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,27 +47,16 @@ pub struct ModernArguments {
|
|||
pub jvm: Vec<ModernArgumentsItem>,
|
||||
}
|
||||
|
||||
pub trait ModernArgumentsExt {
|
||||
pub trait ModernArgumentsItemExt {
|
||||
fn build(&self) -> String;
|
||||
}
|
||||
|
||||
impl ModernArgumentsExt for ModernArguments {
|
||||
impl ModernArgumentsItemExt for [ModernArgumentsItem] {
|
||||
fn build(&self) -> String {
|
||||
// TODO: Implement the custom rules
|
||||
let mut builder = String::new();
|
||||
|
||||
for item in &self.game {
|
||||
match item {
|
||||
ModernArgumentsItem::Simple(s) => {
|
||||
builder.push_str(s);
|
||||
builder.push(' ');
|
||||
},
|
||||
|
||||
ModernArgumentsItem::Rule(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
for item in &self.jvm {
|
||||
for item in self {
|
||||
match item {
|
||||
ModernArgumentsItem::Simple(s) => {
|
||||
builder.push_str(s);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ pub fn libraries_dir() -> crate::Result<PathBuf> {
|
|||
Ok(app_config_dir()?.join("libraries"))
|
||||
}
|
||||
|
||||
pub fn natives_dir() -> crate::Result<PathBuf> {
|
||||
Ok(app_config_dir()?.join("natives"))
|
||||
}
|
||||
|
||||
pub fn java_dir() -> crate::Result<PathBuf> {
|
||||
Ok(app_config_dir()?.join("java"))
|
||||
}
|
||||
|
|
@ -33,3 +37,7 @@ pub fn manifests_dir() -> crate::Result<PathBuf> {
|
|||
pub fn clients_dir() -> crate::Result<PathBuf> {
|
||||
Ok(app_config_dir()?.join("clients"))
|
||||
}
|
||||
|
||||
pub fn game_dir() -> crate::Result<PathBuf> {
|
||||
Ok(app_config_dir()?.join("games"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use sha1::{Digest, Sha1};
|
|||
use std::{
|
||||
fs::File,
|
||||
io::BufReader,
|
||||
path::{Path, PathBuf},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use crate::ErrorKind;
|
||||
|
|
@ -18,7 +18,7 @@ pub fn file_sha1(file: &PathBuf) -> crate::Result<String> {
|
|||
Ok(format!("{:x}", hash))
|
||||
}
|
||||
|
||||
pub fn extract_archive(archive: &Path, dest: &Path) -> crate::Result<()> {
|
||||
pub fn extract_archive(archive: &PathBuf, dest: &PathBuf) -> crate::Result<()> {
|
||||
let ext = match archive.extension() {
|
||||
Some(ext) => ext.to_str().unwrap(),
|
||||
None => return Err(ErrorKind::AnyhowError(anyhow!("unsupported operating system")).into()),
|
||||
|
|
@ -31,7 +31,7 @@ pub fn extract_archive(archive: &Path, dest: &Path) -> crate::Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn extract_zip(archive: &Path, dest: &Path) -> crate::Result<()> {
|
||||
pub fn extract_zip(archive: &PathBuf, dest: &PathBuf) -> crate::Result<()> {
|
||||
let file = File::open(archive).map_err(|err| ErrorKind::IOError(err))?;
|
||||
let archive = zip::ZipArchive::new(file).map_err(|err| ErrorKind::ZipError(err));
|
||||
archive?
|
||||
|
|
@ -40,7 +40,7 @@ pub fn extract_zip(archive: &Path, dest: &Path) -> crate::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn extract_tar_gz(archive: &Path, dest: &Path) -> crate::Result<()> {
|
||||
pub fn extract_tar_gz(archive: &PathBuf, dest: &PathBuf) -> crate::Result<()> {
|
||||
let file = File::open(archive).map_err(|err| ErrorKind::IOError(err))?;
|
||||
let tar_gz = flate2::read::GzDecoder::new(file);
|
||||
let mut archive = tar::Archive::new(tar_gz);
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ pub async fn launch_cluster(
|
|||
uuid: Uuid,
|
||||
) -> Result<(), String> {
|
||||
let manager = &mut state.lock().await.client_manager;
|
||||
manager.launch_cluster(uuid).await?; // TODO: Change how this works
|
||||
manager.launch_cluster(uuid).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -190,8 +190,8 @@ function HomePage() {
|
|||
|
||||
async function newInstance() {
|
||||
await createCluster({
|
||||
name: 'New Cluster',
|
||||
version: '1.8.9',
|
||||
name: 'Modern cluster',
|
||||
version: '1.20.4',
|
||||
client: {
|
||||
type: 'Vanilla',
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue