XCraft/src/launcher.rs
alterwain 842ecf0cf3 modified: src/config.rs
modified:   src/launcher.rs
	modified:   src/main.rs
	modified:   src/www/portable.html
	deleted:    src/www/sign_in.html
2025-03-18 03:37:55 +03:00

388 lines
16 KiB
Rust

use core::str;
use std::io::Cursor;
use base64::Engine;
use base64::prelude::BASE64_STANDARD;
use tokio::fs::File;
use tokio::process::Command;
use tokio::sync::mpsc;
use tokio::sync::mpsc::UnboundedSender;
use crate::config::{LauncherCredentials, LauncherServer};
use crate::minecraft;
use crate::minecraft::session::SignUpResponse;
use crate::minecraft::versions::Version;
use crate::{config::LauncherConfig, minecraft::versions::VersionConfig, util};
const JAVA_ARGS: [&str; 22] = ["-Xms512M",
"-XX:+UnlockExperimentalVMOptions",
"-XX:+DisableExplicitGC",
"-XX:MaxGCPauseMillis=200",
"-XX:+AlwaysPreTouch",
"-XX:+ParallelRefProcEnabled",
"-XX:+UseG1GC",
"-XX:G1NewSizePercent=30",
"-XX:G1MaxNewSizePercent=40",
"-XX:G1HeapRegionSize=8M",
"-XX:G1ReservePercent=20",
"-XX:InitiatingHeapOccupancyPercent=15",
"-XX:G1HeapWastePercent=5",
"-XX:G1MixedGCCountTarget=4",
"-XX:G1MixedGCLiveThresholdPercent=90",
"-XX:G1RSetUpdatingPauseTimePercent=5",
"-XX:+UseStringDeduplication", "-Dfile.encoding=UTF-8", "-Dfml.ignoreInvalidMinecraftCertificates=true", "-Dfml.ignorePatchDiscrepancies=true", "-Djava.net.useSystemProxies=true", "-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump"];
#[derive(Default)]
pub struct Launcher {
pub config: LauncherConfig,
}
impl Launcher {
pub fn is_portable(&self) -> bool {
crate::config::get_relative_launcher_dir().exists()
}
pub fn is_config_exist(&self) -> bool {
self.config.config_path().exists()
}
pub fn load_config(&mut self) {
if self.is_config_exist() {
self.config = toml::from_str(
str::from_utf8(&std::fs::read(self.config.config_path()).unwrap()).unwrap()).unwrap();
}
}
pub fn save_config(&self) {
let _ = std::fs::write(self.config.config_path(), toml::to_string_pretty(&self.config).unwrap());
}
pub fn init_config(&mut self, user_name: String) {
self.load_config();
self.config.set_username(user_name);
self.save_config();
}
fn save_server_info(&mut self, uuid: String, username: String, password: String, domain: String, session_server_port: u16, server_port: u16) -> (bool, &str) {
self.config.add_server(LauncherServer {
domain,
port: server_port,
session_server_port,
credentials: LauncherCredentials {
uuid,
username,
password
}
});
self.save_config();
(true, "You are successfully registered")
}
pub async fn register_user_server(&mut self, server: String, username: String, password: String) -> (bool, &str) {
let mut session_server_port: u16 = 8999;
let mut server_port: u16 = 25565;
let mut domain = server.clone();
if let Some(index) = server.find("#") {
let (a,b) = server.split_at(index+1);
session_server_port = b.parse().unwrap();
domain = a[..a.len()-1].to_string();
}
if let Some(index) = domain.find(":") {
let dmc = domain.clone();
let (a,b) = dmc.split_at(index+1);
domain = a[..a.len()-1].to_string();
server_port = b.parse().unwrap();
}
println!("Server information: {}:{} session={}", domain, server_port, session_server_port);
match minecraft::session::try_signup(domain.clone(), session_server_port, username.clone(), password.clone()).await {
Ok(status) => match status {
SignUpResponse::ServerError => (false, "Internal server error"),
SignUpResponse::BadCredentials => (false, "Username or password is not valid"),
SignUpResponse::UserAlreadyExists => (false, "User already exists"),
SignUpResponse::Registered(uuid) => self.save_server_info(uuid, username, password, domain, session_server_port, server_port)
}
Err(_e) => (false, "Internal server error")
}
}
pub async fn get_servers_list(&self) -> Vec<(String, String, Option<String>)> {
let mut v = Vec::new();
let servers = self.config.servers();
for server in servers {
v.push((server.domain.clone(), server.credentials.username.clone(), minecraft::server::get_server_icon(&server.domain, server.port).await.unwrap_or(None)));
}
v
}
pub fn get_instances_list(&self) -> Vec<(String, String, String)> {
let mut v = Vec::new();
let mut instances = self.config.launcher_dir();
instances.push("instances");
if let Ok(entries) = std::fs::read_dir(instances) {
for entry in entries {
if entry.is_err() { continue; }
let entry = entry.unwrap();
if !entry.metadata().unwrap().is_dir() { continue; }
let mut p = entry.path();
p.push("client.json");
if let Ok(data) = std::fs::read(p) {
let config: VersionConfig = serde_json::from_slice(&data).unwrap();
let c_type = config.r#type;
let c_type = c_type.as_str();
v.push((config.id, c_type.to_string(), format!("data:image/png;base64,{}", BASE64_STANDARD.encode(match c_type {
"old_alpha" => include_bytes!("www/icons/alpha.png").to_vec(),
"old_beta" => include_bytes!("www/icons/beta.png").to_vec(),
"release" | "snapshot" => include_bytes!("www/icons/release.png").to_vec(),
_ => include_bytes!("www/icons/glowstone.png").to_vec()
}))));
}
}
}
v
}
pub fn get_screenshots(&self) -> Vec<(String, String)> {
let mut v = Vec::new();
let mut instances = self.config.launcher_dir();
instances.push("instances");
if let Ok(entries) = std::fs::read_dir(instances) {
for entry in entries {
if entry.is_err() { continue; }
let entry = entry.unwrap();
if !entry.metadata().unwrap().is_dir() { continue; }
let mut p = entry.path();
p.push("data");
p.push("screenshots");
if !p.exists() { continue; }
if let Ok(screenshots) = std::fs::read_dir(p) {
for screenshot in screenshots {
if let Ok(screenshot) = screenshot {
if screenshot.file_name().to_str().unwrap().ends_with("png") {
v.push((screenshot.path().to_str().unwrap().to_string(), format!("data:image/png;base64,{}", BASE64_STANDARD.encode(std::fs::read(screenshot.path()).unwrap()))));
}
}
}
}
}
}
v
}
pub async fn launch_instance(&self, instance_name: String, username: String, uuid: String, token: String) {
let mut instances = self.config.launcher_dir();
instances.push("instances");
instances.push(&instance_name);
instances.push("client.json");
let mut client_jar = self.config.launcher_dir();
client_jar.push("instances");
client_jar.push(&instance_name);
client_jar.push("client.jar");
let mut instance_dir = self.config.launcher_dir();
instance_dir.push("instances");
instance_dir.push(&instance_name);
instance_dir.push("data");
let mut cmd = Command::new(&self.config.java_path);
cmd.current_dir(instance_dir);
cmd.stdout(std::process::Stdio::piped());
for arg in JAVA_ARGS {
cmd.arg(arg.to_string());
}
cmd.arg(["-Xmx", &self.config.ram_amount.to_string(), "M"].concat());
let mut natives_path = self.config.launcher_dir();
natives_path.push("instances");
natives_path.push(&instance_name);
natives_path.push("natives");
cmd.arg(["-Djava.library.path=", natives_path.to_str().unwrap() ].concat());
cmd.arg(["-Dminecraft.client.jar=", client_jar.to_str().unwrap()].concat());
cmd.arg("-cp".to_string());
if let Ok(data) = std::fs::read(&instances) {
let config: VersionConfig = serde_json::from_slice(&data).unwrap();
let mut libraries_cmd = Vec::new();
for library in config.libraries {
if let Some(classifier) = &library.downloads.classifiers {
if let Some(natives) = &classifier.natives {
let rel_path = &natives.path;
let mut libs = self.config.launcher_dir();
libs.push("libraries");
let rel_path = [libs.to_str().unwrap(), "\\", &rel_path.replace("/", "\\")].concat();
let data = std::fs::read(rel_path).unwrap();
zip_extract::extract(Cursor::new(data), &natives_path, true);
}
} else {
let mut libs = self.config.launcher_dir();
libs.push("libraries");
libs.push(library.to_pathbuf_file());
libraries_cmd.push([libs.to_str().unwrap(), ";"].concat());
}
}
libraries_cmd.push(client_jar.to_str().unwrap().to_string());
cmd.arg(libraries_cmd.concat());
cmd.arg(config.mainClass.clone());
let mut game_dir = self.config.launcher_dir();
game_dir.push("instances");
game_dir.push(&instance_name);
game_dir.push("data");
let mut assets_dir = self.config.launcher_dir();
assets_dir.push("assets");
cmd.args(&["--username", &username, "--version", &instance_name, "--gameDir", game_dir.to_str().unwrap(), "--assetsDir", assets_dir.to_str().unwrap(), "--assetIndex", &config.assetIndex.id, "--uuid", &uuid, "--accessToken", &token, "--userProperties", "{}", "--userType", "mojang", "--width", "925", "--height", "530"]);
let child = cmd.spawn().unwrap();
/*if let Some(stdout) = child.stdout.take() {
let reader = BufReader::new(stdout);
let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
println!("Line: {}", line);
}
}*/
}
}
pub async fn new_vanilla_instance(&mut self, config: VersionConfig, version_object: &Version, sender: UnboundedSender<(u8, String)>) {
let (sx, mut rx) = mpsc::unbounded_channel();
let root = self.config.launcher_dir();
let mut instances = root.clone();
instances.push("instances");
instances.push(&config.id);
let _ = std::fs::create_dir_all(&instances);
instances.push("client.jar");
let mut overall_size = config.downloads.client.size as usize;
let mut cnt = 0;
let client_jar_url = config.downloads.client.url;
let mut client_json_path = root.clone();
client_json_path.push("instances");
client_json_path.push(config.id);
client_json_path.push("client.json");
let _ = util::download_file(&version_object.url, client_json_path.to_str().unwrap(), sx.clone(), "Downloading client.json");
cnt += 1;
let _ = util::download_file(&client_jar_url, instances.to_str().unwrap(), sx.clone(), "Downloading client.jar");
cnt += 1;
let mut libraries = root.clone();
libraries.push("libraries");
for i in 0..config.libraries.len() {
let library = &config.libraries[i];
if let Some(artifact) = &library.downloads.artifact {
let mut dl_path = libraries.clone();
let mut dl_pp = libraries.clone();
dl_pp.push(library.to_pathbuf_path());
let _ = std::fs::create_dir_all(dl_pp);
dl_path.push(library.to_pathbuf_file());
if File::open(dl_path.to_str().unwrap()).await.is_err() {
overall_size += artifact.size as usize;
let _ = util::download_file(&artifact.url, dl_path.to_str().unwrap(), sx.clone(), "Downloading libraries");
cnt += 1;
}
}
if let Some(classifiers) = &library.downloads.classifiers {
if let Some(natives) = &classifiers.natives {
let mut dl_path = libraries.clone();
dl_path.push(&natives.path);
let t_p = dl_path.to_str().unwrap().split("/").collect::<Vec<&str>>();
let t_p = t_p[..t_p.len()-1].join("/");
let _ = std::fs::create_dir_all(&t_p);
if File::open(dl_path.to_str().unwrap()).await.is_err() {
overall_size += natives.size as usize;
let _ = util::download_file(&natives.url, dl_path.to_str().unwrap(), sx.clone(), "Downloading natives");
cnt += 1;
}
}
}
}
let mut assets_path = root.clone();
assets_path.push("assets");
let mut indexes = assets_path.clone();
indexes.push("indexes");
let _ = std::fs::create_dir_all(indexes);
let mut objects = assets_path.clone();
objects.push("objects");
let _ = std::fs::create_dir_all(objects);
let mut index = assets_path.clone();
index.push(config.assetIndex.to_path());
let _ = util::download_file(&config.assetIndex.url, index.to_str().unwrap(), sx.clone(), "Downloading assets indexes");
cnt += 1;
let asset_index = config.assetIndex.url;
overall_size += config.assetIndex.size as usize;
overall_size += config.assetIndex.totalSize as usize;
let assets = crate::minecraft::assets::fetch_assets_list(&asset_index).await.unwrap().objects;
for (_key, asset) in assets {
let mut single_object = assets_path.clone();
single_object.push(asset.to_path());
let mut single_object_path = assets_path.clone();
single_object_path.push(asset.to_small_path());
std::fs::create_dir_all(single_object_path);
if File::open(single_object.to_str().unwrap()).await.is_err() {
util::download_file(&asset.to_url(), single_object.to_str().unwrap(), sx.clone(), "Downloading assets objects");
cnt += 1;
}
}
tokio::spawn(async move {
let mut current_size = 0;
let mut current_cnt = 0;
while let Some((size, status)) = rx.recv().await {
current_size += size;
current_cnt += 1;
sender.send((((current_size as f32 / overall_size as f32) * 100.0) as u8, status));
if current_cnt >= cnt {
sender.send((100, "_".to_string()));
}
}
});
}
pub fn init_dirs(&self) {
let root = self.config.launcher_dir();
std::fs::create_dir_all(&root);
// instances assets libraries config.toml
let mut instances = root.clone();
instances.push("instances");
let mut assets = root.clone();
assets.push("assets");
let mut libraries = root.clone();
libraries.push("libraries");
std::fs::create_dir_all(&instances);
std::fs::create_dir_all(&assets);
std::fs::create_dir_all(&libraries);
}
}