XCraft/src/main.rs
2025-03-15 03:08:57 +03:00

179 lines
8.3 KiB
Rust

use std::sync::{Arc, Mutex};
use config::LauncherConfig;
use launcher::Launcher;
use serde::{Deserialize, Serialize};
use tokio::runtime::Runtime;
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
use winit::application::ApplicationHandler;
use winit::event_loop::{ControlFlow, EventLoop};
use winit::event::{Event, WindowEvent};
use winit::platform::run_on_demand::EventLoopExtRunOnDemand;
use winit::window::{Window, WindowId};
use winit::event_loop::ActiveEventLoop;
use wry::dpi::LogicalSize;
use wry::http::{version, Request, Response};
use wry::{RequestAsyncResponder, WebView, WebViewBuilder};
mod config;
mod launcher;
mod util;
mod minecraft;
static SENDER: Mutex<Option<UnboundedSender<(String, Option<UIMessage>, RequestAsyncResponder)>>> = Mutex::new(None);
#[derive(Serialize, Deserialize)]
struct UIMessage {
params: Vec<String>
}
#[derive(Default)]
struct App {
window: Option<Window>,
webview: Option<WebView>
}
impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window = event_loop.create_window(Window::default_attributes().with_inner_size(LogicalSize::new(900, 600)).with_min_inner_size(LogicalSize::new(900, 600)).with_title("XCraft")).unwrap();
let webview = WebViewBuilder::new()
.with_asynchronous_custom_protocol("xcraft".into(), move |wid, request, responder| {
let uri = request.uri().to_string();
if let Ok(msg) = serde_json::from_slice(request.body()) {
let _ = SENDER.lock().unwrap().as_ref().unwrap().send((uri, Some(msg), responder));
return;
}
let _ = SENDER.lock().unwrap().as_ref().unwrap().send((uri, None, responder));
})
.with_url("xcraft://custom/ui")
.build(&window)
.unwrap();
self.window = Some(window);
self.webview = Some(webview);
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {
match event {
WindowEvent::CloseRequested => {
event_loop.exit();
},
_ => {}
}
}
}
#[tokio::main]
async fn main() {
let event_loop = EventLoop::new().unwrap();
let mut app = App::default();
let rt = Runtime::new().unwrap();
rt.spawn(async move {
let (snd, mut receiver) = mpsc::unbounded_channel();
*SENDER.lock().unwrap() = Some(snd);
let mut launcher = Launcher::default();
let (sx, mut dl_rec) = mpsc::unbounded_channel();
loop {
if let Some((ui_action, params, responder)) = receiver.recv().await {
let ui_action = &ui_action[16..];
match ui_action {
"ui" => responder.respond(Response::new(include_bytes!("www/portable.html"))),
"portable" => {
launcher.config.set_portable(true);
launcher.init_dirs();
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["show_login".to_string()] }).unwrap()))
}
"installation" => {
launcher.init_dirs();
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["show_login".to_string()] }).unwrap()))
}
"check_installation" => {
if launcher.is_portable() {
launcher.config.set_portable(true);
launcher.init_dirs();
if !launcher.is_config_exist() {
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["show_login".to_string()] }).unwrap()))
} else {
launcher.load_config();
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["show_add".to_string(), "sidebar_on".to_string()] }).unwrap()))
}
}
}
"sign_up" => {
let user_name = params.as_ref().unwrap().params.get(0).unwrap();
launcher.init_config(user_name.to_string());
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["show_add".to_string(), "sidebar_on".to_string()] }).unwrap()));
}
"fetch_official_versions" => {
if let Ok(versions) = crate::minecraft::versions::fetch_versions_list().await {
let versions: Vec<String> = versions.versions.iter().map(|t| t.id.clone()).collect();
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: [ vec!["set_downloadable_versions".to_string()], versions ].concat() }).unwrap()));
} else {
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: Vec::new() }).unwrap()));
}
}
"download_vanilla" => {
let version = params.unwrap().params[0].clone();
println!("Version: {}", version);
if let Ok(versions) = crate::minecraft::versions::fetch_versions_list().await {
println!("Got versions");
let version = versions.versions.iter().find(|t| t.id.clone() == version);
if let Some(version) = version {
println!("Found");
match crate::minecraft::versions::fetch_version_object(version).await {
Ok(config ) => {
println!("Config: {}", config.id);
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, version, sx.clone()).await;
}
Err(e) => {
println!("Error: {}", e);
}
}
}
}
}
"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" => {
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()));
} else {
responder.respond(Response::new(vec![]));
}
}
"run_instance" => {
let instance_name = params.unwrap().params[0].clone();
launcher.launch_instance(instance_name).await;
}
"locate_java" => {
if let Ok(java_path) = java_locator::locate_file("java.exe") {
launcher.config.java_path = java_path.clone();
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["locate_java".to_string(), [&java_path, "java.exe"].join("\\")] }).unwrap()));
} else {
// todo: implement error notifications
}
}
_ => {}
}
}
}
});
event_loop.run_app(&mut app).unwrap();
}