Downloading completed, added instances section
modified: Cargo.lock modified: Cargo.toml modified: src/config.rs modified: src/launcher.rs modified: src/main.rs modified: src/minecraft.rs modified: src/util.rs new file: src/www/icons/alpha.png new file: src/www/icons/new_era.png new file: src/www/icons/release.png modified: src/www/portable.html
This commit is contained in:
parent
859a3902aa
commit
ac7fedc16a
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -6,6 +6,7 @@ version = 4
|
|||||||
name = "CraftX"
|
name = "CraftX"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
"dirs",
|
"dirs",
|
||||||
"futures",
|
"futures",
|
||||||
"rand 0.9.0",
|
"rand 0.9.0",
|
||||||
|
@ -13,4 +13,5 @@ rand = "0.9.0"
|
|||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
toml = "0.8.20"
|
toml = "0.8.20"
|
||||||
surf = { version = "2.3.2", features = ["hyper-client"] }
|
surf = { version = "2.3.2", features = ["hyper-client"] }
|
||||||
|
base64 = "0.22.1"
|
@ -2,6 +2,8 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Default, Serialize, Deserialize)]
|
#[derive(Default, Serialize, Deserialize)]
|
||||||
pub struct LauncherConfig {
|
pub struct LauncherConfig {
|
||||||
is_portable: bool,
|
is_portable: bool,
|
||||||
|
108
src/launcher.rs
108
src/launcher.rs
@ -1,7 +1,10 @@
|
|||||||
use core::str;
|
use core::str;
|
||||||
use std::sync::Arc;
|
use base64::{encode, Engine};
|
||||||
use tokio::sync::{mpsc, Mutex};
|
use base64::prelude::BASE64_STANDARD;
|
||||||
use tokio::sync::mpsc::{Sender, UnboundedReceiver, UnboundedSender};
|
use tokio::fs::File;
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
|
use crate::minecraft::versions::Version;
|
||||||
use crate::{config::LauncherConfig, minecraft::versions::VersionConfig, util};
|
use crate::{config::LauncherConfig, minecraft::versions::VersionConfig, util};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -27,7 +30,7 @@ impl Launcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn save_config(&self) {
|
pub fn save_config(&self) {
|
||||||
std::fs::write(self.config.config_path(), toml::to_string_pretty(&self.config).unwrap());
|
let _ = std::fs::write(self.config.config_path(), toml::to_string_pretty(&self.config).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_config(&mut self, user_name: String) {
|
pub fn init_config(&mut self, user_name: String) {
|
||||||
@ -37,24 +40,54 @@ impl Launcher {
|
|||||||
self.save_config();
|
self.save_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn new_vanilla_instance(&mut self, config: VersionConfig, sender: UnboundedSender<(u8, String)>) {
|
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();
|
||||||
|
v.push((config.id, config.r#type, format!("data:image/png;base64,{}", BASE64_STANDARD.encode(include_bytes!("www/icons/alpha.png")))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (sx, mut rx) = mpsc::unbounded_channel();
|
||||||
|
|
||||||
let root = self.config.launcher_dir();
|
let root = self.config.launcher_dir();
|
||||||
let mut instances = root.clone();
|
let mut instances = root.clone();
|
||||||
instances.push("instances");
|
instances.push("instances");
|
||||||
instances.push(config.id);
|
instances.push(&config.id);
|
||||||
|
|
||||||
std::fs::create_dir_all(&instances);
|
let _ = std::fs::create_dir_all(&instances);
|
||||||
|
|
||||||
instances.push("client.jar");
|
instances.push("client.jar");
|
||||||
|
|
||||||
let mut overall_size = config.downloads.client.size as usize;
|
let mut overall_size = config.downloads.client.size as usize;
|
||||||
|
let mut cnt = 0;
|
||||||
|
|
||||||
let client_jar_url = config.downloads.client.url;
|
let client_jar_url = config.downloads.client.url;
|
||||||
|
|
||||||
util::download_file(&client_jar_url, instances.to_str().unwrap(), config.downloads.client.size, sx.clone(), "Downloading client.jar");
|
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();
|
let mut libraries = root.clone();
|
||||||
libraries.push("libraries");
|
libraries.push("libraries");
|
||||||
@ -66,17 +99,72 @@ impl Launcher {
|
|||||||
let mut dl_path = libraries.clone();
|
let mut dl_path = libraries.clone();
|
||||||
let mut dl_pp = libraries.clone();
|
let mut dl_pp = libraries.clone();
|
||||||
dl_pp.push(library.to_pathbuf_path());
|
dl_pp.push(library.to_pathbuf_path());
|
||||||
std::fs::create_dir_all(dl_pp);
|
let _ = std::fs::create_dir_all(dl_pp);
|
||||||
dl_path.push(library.to_pathbuf_file());
|
dl_path.push(library.to_pathbuf_file());
|
||||||
util::download_file(&artifact.url, dl_path.to_str().unwrap(), config.downloads.client.size, sx.clone(), "Downloading libraries");
|
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 {
|
||||||
|
overall_size += natives.size as usize;
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
|
||||||
|
util::download_file(&asset.to_url(), single_object.to_str().unwrap(), sx.clone(), "Downloading assets objects");
|
||||||
|
cnt += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut current_size = 0;
|
let mut current_size = 0;
|
||||||
|
let mut current_cnt = 0;
|
||||||
while let Some((size, status)) = rx.recv().await {
|
while let Some((size, status)) = rx.recv().await {
|
||||||
current_size += size;
|
current_size += size;
|
||||||
|
current_cnt += 1;
|
||||||
sender.send((((current_size as f32 / overall_size as f32) * 100.0) as u8, status));
|
sender.send((((current_size as f32 / overall_size as f32) * 100.0) as u8, status));
|
||||||
|
if current_cnt >= cnt {
|
||||||
|
sender.send((100, "_".to_string()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
16
src/main.rs
16
src/main.rs
@ -82,7 +82,7 @@ async fn main() {
|
|||||||
if let Some((ui_action, params, responder)) = receiver.recv().await {
|
if let Some((ui_action, params, responder)) = receiver.recv().await {
|
||||||
let ui_action = &ui_action[16..];
|
let ui_action = &ui_action[16..];
|
||||||
match ui_action {
|
match ui_action {
|
||||||
"ui" => responder.respond(Response::new(include_str!("www/portable.html").as_bytes())),
|
"ui" => responder.respond(Response::new(include_bytes!("www/portable.html"))),
|
||||||
"portable" => {
|
"portable" => {
|
||||||
launcher.config.set_portable(true);
|
launcher.config.set_portable(true);
|
||||||
launcher.init_dirs();
|
launcher.init_dirs();
|
||||||
@ -128,7 +128,7 @@ async fn main() {
|
|||||||
Ok(config ) => {
|
Ok(config ) => {
|
||||||
println!("Config: {}", config.id);
|
println!("Config: {}", config.id);
|
||||||
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["show_loading".to_string(), "sidebar_off".to_string()] }).unwrap()));
|
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["show_loading".to_string(), "sidebar_off".to_string()] }).unwrap()));
|
||||||
launcher.new_vanilla_instance(config, sx.clone()).await;
|
launcher.new_vanilla_instance(config, version, sx.clone()).await;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error: {}", e);
|
println!("Error: {}", e);
|
||||||
@ -137,6 +137,18 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"fetch_instances_list" => {
|
||||||
|
let resp = launcher.get_instances_list();
|
||||||
|
let mut v: Vec<String> = Vec::new();
|
||||||
|
v.push("set_instances_list".to_string());
|
||||||
|
for (id, release_type, img) in resp {
|
||||||
|
v.push(id);
|
||||||
|
v.push(release_type);
|
||||||
|
v.push(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: v }).unwrap()));
|
||||||
|
}
|
||||||
"check_download_status" => {
|
"check_download_status" => {
|
||||||
if let Ok((percent, text)) = dl_rec.try_recv() {
|
if let Ok((percent, text)) = dl_rec.try_recv() {
|
||||||
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["update_downloads".to_string(), text, percent.to_string()] }).unwrap()));
|
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["update_downloads".to_string(), text, percent.to_string()] }).unwrap()));
|
||||||
|
@ -33,6 +33,7 @@ pub mod versions {
|
|||||||
pub mainClass: String,
|
pub mainClass: String,
|
||||||
pub downloads: ConfigDownloads,
|
pub downloads: ConfigDownloads,
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
pub r#type: String,
|
||||||
pub libraries: Vec<VersionLibrary>
|
pub libraries: Vec<VersionLibrary>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,9 +78,24 @@ pub mod versions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct LibraryClassifiers {
|
||||||
|
#[serde(rename = "natives-windows")]
|
||||||
|
pub natives: Option<LibraryNatives>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct LibraryNatives {
|
||||||
|
pub path: String,
|
||||||
|
pub sha1: String,
|
||||||
|
pub size: u64,
|
||||||
|
pub url: String
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct LibraryDownloads {
|
pub struct LibraryDownloads {
|
||||||
pub artifact: Option<LibraryArtifact>
|
pub artifact: Option<LibraryArtifact>,
|
||||||
|
pub classifiers: Option<LibraryClassifiers>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@ -107,9 +123,19 @@ pub mod versions {
|
|||||||
pub id: String,
|
pub id: String,
|
||||||
pub sha1: String,
|
pub sha1: String,
|
||||||
pub totalSize: u64,
|
pub totalSize: u64,
|
||||||
|
pub size: u64,
|
||||||
pub url: String
|
pub url: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ConfigAssetIndex {
|
||||||
|
pub fn to_path(&self) -> PathBuf {
|
||||||
|
let mut p = PathBuf::new();
|
||||||
|
p.push("indexes");
|
||||||
|
p.push([&self.id, ".json"].concat());
|
||||||
|
p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn fetch_versions_list() -> Result<VersionManifest, Box<dyn Error + Send + Sync>> {
|
pub async fn fetch_versions_list() -> Result<VersionManifest, Box<dyn Error + Send + Sync>> {
|
||||||
let mut r = surf::get("https://piston-meta.mojang.com/mc/game/version_manifest_v2.json").await?;
|
let mut r = surf::get("https://piston-meta.mojang.com/mc/game/version_manifest_v2.json").await?;
|
||||||
let resp = r.body_bytes().await.unwrap();
|
let resp = r.body_bytes().await.unwrap();
|
||||||
@ -124,4 +150,48 @@ pub mod versions {
|
|||||||
let resp: VersionConfig = serde_json::from_slice(&resp)?;
|
let resp: VersionConfig = serde_json::from_slice(&resp)?;
|
||||||
Ok(resp)
|
Ok(resp)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod assets {
|
||||||
|
use std::{collections::HashMap, error::Error, path::PathBuf};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct SingleAsset {
|
||||||
|
pub hash: String,
|
||||||
|
pub sha1: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SingleAsset {
|
||||||
|
pub fn to_url(&self) -> String {
|
||||||
|
["https://resources.download.minecraft.net/", &self.hash[..2], "/", &self.hash].concat()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_path(&self) -> PathBuf {
|
||||||
|
let mut p = PathBuf::new();
|
||||||
|
p.push("objects");
|
||||||
|
p.push(&self.hash[..2]);
|
||||||
|
p.push(&self.hash);
|
||||||
|
p
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_small_path(&self) -> PathBuf {
|
||||||
|
let mut p = PathBuf::new();
|
||||||
|
p.push("objects");
|
||||||
|
p.push(&self.hash[..2]);
|
||||||
|
p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct Assets {
|
||||||
|
pub objects: HashMap<String, SingleAsset>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn fetch_assets_list(url: &str) -> Result<Assets, Box<dyn Error + Send + Sync>> {
|
||||||
|
let mut r = surf::get(url).await?;
|
||||||
|
let resp = r.body_bytes().await.unwrap();
|
||||||
|
let resp: Assets = serde_json::from_slice(&resp)?;
|
||||||
|
Ok(resp)
|
||||||
|
}
|
||||||
}
|
}
|
40
src/util.rs
40
src/util.rs
@ -1,10 +1,7 @@
|
|||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use futures::AsyncReadExt;
|
use futures::AsyncReadExt;
|
||||||
use rand::{distr::Alphanumeric, Rng};
|
use rand::{distr::Alphanumeric, Rng};
|
||||||
use tokio::{fs::File, io::AsyncWriteExt};
|
use tokio::{fs::File, io::AsyncWriteExt};
|
||||||
use tokio::sync::mpsc::{Sender, UnboundedReceiver, UnboundedSender};
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
use tokio::sync::{mpsc};
|
|
||||||
use crate::launcher::Launcher;
|
|
||||||
|
|
||||||
pub fn random_string(len: usize) -> String {
|
pub fn random_string(len: usize) -> String {
|
||||||
rand::rng()
|
rand::rng()
|
||||||
@ -14,29 +11,30 @@ pub fn random_string(len: usize) -> String {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn download_file(url: &str, file_path: &str, size: u64, sender: UnboundedSender<(usize, String)>, status: &str) -> Result<(), Box<dyn std::error::Error>> {
|
pub fn download_file(url: &str, file_path: &str, sender: UnboundedSender<(usize, String)>, status: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let url = url.to_string();
|
let url = url.to_string();
|
||||||
let file_path = file_path.to_string();
|
let file_path = file_path.to_string();
|
||||||
let status = status.to_string();
|
let status = status.to_string();
|
||||||
tokio::spawn( async move {
|
tokio::spawn( async move {
|
||||||
let mut res = surf::get(url).await.unwrap();
|
if let Ok(mut res) = surf::get(url).await {
|
||||||
|
let mut downloaded = 0;
|
||||||
let total_size = res.len().unwrap_or(0); // Total size in bytes (if available)
|
let mut buf = vec![0; 8192]; // Buffer for reading chunks
|
||||||
let mut downloaded = 0;
|
|
||||||
let mut buf = vec![0; 8192]; // Buffer for reading chunks
|
|
||||||
|
|
||||||
let mut file = File::create(file_path).await.unwrap();
|
|
||||||
|
|
||||||
let mut r= res.take_body().into_reader();
|
|
||||||
while let Ok(n) = r.read(&mut buf).await {
|
|
||||||
if n == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
downloaded += n;
|
|
||||||
|
|
||||||
file.write(&buf[..n]).await;
|
let mut file = File::create(file_path).await.unwrap();
|
||||||
|
|
||||||
|
let mut r= res.take_body().into_reader();
|
||||||
|
while let Ok(n) = r.read(&mut buf).await {
|
||||||
|
if n == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
downloaded += n;
|
||||||
|
|
||||||
|
let _ = file.write(&buf[..n]).await;
|
||||||
|
}
|
||||||
|
let _ = sender.send((downloaded, status.clone()));
|
||||||
|
} else {
|
||||||
|
let _ = sender.send((0, status.clone()));
|
||||||
}
|
}
|
||||||
sender.send((downloaded, status.clone()));
|
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
BIN
src/www/icons/alpha.png
Normal file
BIN
src/www/icons/alpha.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
src/www/icons/new_era.png
Normal file
BIN
src/www/icons/new_era.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 263 B |
BIN
src/www/icons/release.png
Normal file
BIN
src/www/icons/release.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 266 B |
@ -27,7 +27,7 @@
|
|||||||
<div class="py-4">
|
<div class="py-4">
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
onclick="showSection(this, 'instances')"
|
onclick="showSection(this, 'instances'); showInstancesSection()"
|
||||||
class="menu-btn t group relative flex justify-center rounded-sm bg-green-50 px-2 py-1.5 text-green-700"
|
class="menu-btn t group relative flex justify-center rounded-sm bg-green-50 px-2 py-1.5 text-green-700"
|
||||||
>
|
>
|
||||||
<i class="fa-solid fa-gamepad"></i>
|
<i class="fa-solid fa-gamepad"></i>
|
||||||
@ -158,7 +158,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-center items-center w-screen h-screen">
|
<div class="flex justify-center items-center w-screen h-screen">
|
||||||
<div id="loading-section" class="bg-white shadow-lg rounded-xl p-6 w-96 text-center hidden">
|
<div id="loading-section" class="xsection bg-white shadow-lg rounded-xl p-6 w-96 text-center hidden">
|
||||||
<h2 class="text-2xl font-semibold text-gray-700">Launching Minecraft</h2>
|
<h2 class="text-2xl font-semibold text-gray-700">Launching Minecraft</h2>
|
||||||
|
|
||||||
<!-- Loading Status -->
|
<!-- Loading Status -->
|
||||||
@ -354,7 +354,7 @@
|
|||||||
|
|
||||||
<div id="instances-section" class="xsection grid grid-cols-3 gap-4 p-6 w-fill hidden">
|
<div id="instances-section" class="xsection grid grid-cols-3 gap-4 p-6 w-fill hidden">
|
||||||
<div class="bg-white cursor-pointer hover:bg-green-500 hover:text-white shadow-lg rounded-xl w-48 h-24 flex justify-center items-center">
|
<div class="bg-white cursor-pointer hover:bg-green-500 hover:text-white shadow-lg rounded-xl w-48 h-24 flex justify-center items-center">
|
||||||
<img src="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fi.pinimg.com%2Foriginals%2Fc0%2F84%2Ff1%2Fc084f19e45602b0a56107ec87149c3a5.png&f=1&nofb=1&ipt=7a22d9bd0f8ec98f71cdee3b26ba45c8eeb4da5286e17337d5550291e1ad1dda&ipo=images" class="w-12 h-12 rounded-full">
|
<img src="" class="w-12 h-12 rounded-full">
|
||||||
<div class="h-fill ms-2">
|
<div class="h-fill ms-2">
|
||||||
<h2 class="text-lg font-semibold">Release</h2>
|
<h2 class="text-lg font-semibold">Release</h2>
|
||||||
<h2 class="text-sm font-semibold">1.12.2</h2>
|
<h2 class="text-sm font-semibold">1.12.2</h2>
|
||||||
@ -393,20 +393,48 @@
|
|||||||
if( params[i] == "show_add" ) {
|
if( params[i] == "show_add" ) {
|
||||||
showAddSection();
|
showAddSection();
|
||||||
} else if( params[i] == "set_downloadable_versions" ) {
|
} else if( params[i] == "set_downloadable_versions" ) {
|
||||||
setDownloadableVersions(params.slice(1));
|
setDownloadableVersions(params.slice(i+1));
|
||||||
break;
|
break;
|
||||||
} else if( params[i] == "update_downloads" ) {
|
} else if( params[i] == "update_downloads" ) {
|
||||||
updateDownloads(params.slice(1));
|
updateDownloads(params.slice(i+1));
|
||||||
break;
|
break;
|
||||||
|
} else if( params[i] == "show_instances") {
|
||||||
|
showInstancesSection();
|
||||||
|
} else if( params[i] == "set_instances_list" ) {
|
||||||
|
setInstancesList(params.slice(i+1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setInstancesList(params) {
|
||||||
|
$("#instances-section").html("");
|
||||||
|
for( let i = 0; i < params.length; i+=3 ) {
|
||||||
|
let instance = `<div onclick="runInstance('`+params[i]+`')" class="bg-white cursor-pointer hover:bg-green-500 hover:text-white shadow-lg rounded-xl w-48 h-24 flex justify-center items-center">
|
||||||
|
<img src="`+params[i+2]+`" class="w-12 h-12 rounded-full">
|
||||||
|
<div class="h-fill ms-2">
|
||||||
|
<h2 class="text-lg font-semibold">` + params[i+1] + `</h2>
|
||||||
|
<h2 class="text-sm font-semibold">` + params[i] + `</h2>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
$("#instances-section").append(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateDownloads(params) {
|
function updateDownloads(params) {
|
||||||
let text = params[0];
|
let text = params[0];
|
||||||
let percentage = params[1];
|
let percentage = params[1];
|
||||||
$("#loading-text").html(text);
|
if( text != "_" ) {
|
||||||
$("#progress-bar").css("width", percentage+"%");
|
$("#loading-text").html(text);
|
||||||
|
$("#progress-bar").css("width", percentage+"%");
|
||||||
|
} else {
|
||||||
|
$("#sidebar").removeClass('hidden');
|
||||||
|
showSection(undefined, "instances");
|
||||||
|
showInstancesSection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showInstancesSection() {
|
||||||
|
$.post({url: "fetch_instances_list" }, processParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadSelectedVersion() {
|
function downloadSelectedVersion() {
|
||||||
@ -457,8 +485,10 @@
|
|||||||
$.get("check_installation", processParams);
|
$.get("check_installation", processParams);
|
||||||
|
|
||||||
setInterval(function() {
|
setInterval(function() {
|
||||||
$.get("check_download_status", processParams);
|
if( !$("#loading-section").hasClass("hidden") ) {
|
||||||
}, 100);
|
$.get("check_download_status", processParams);
|
||||||
|
}
|
||||||
|
}, 10);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user