modified: src/config.rs

modified:   src/launcher.rs
	modified:   src/main.rs
	modified:   src/www/portable.html
	deleted:    src/www/sign_in.html
This commit is contained in:
Michael Wain 2025-03-18 03:37:55 +03:00
parent d64a0e494f
commit 842ecf0cf3
5 changed files with 389 additions and 446 deletions

View File

@ -26,12 +26,13 @@ pub struct LauncherConfig {
pub show_beta: bool,
pub show_snapshots: bool,
pub ram_amount: u32,
pub enable_blur: bool,
servers: Vec<LauncherServer>
}
impl Default for LauncherConfig {
fn default() -> Self {
Self { is_portable: Default::default(), user_name: Default::default(), java_path: "java".to_string(), show_alpha: true, show_beta: true, show_snapshots: false, ram_amount: 1024, servers: Default::default() }
Self { is_portable: Default::default(), user_name: Default::default(), java_path: "java".to_string(), show_alpha: true, show_beta: true, show_snapshots: false, ram_amount: 1024, servers: Default::default(), enable_blur: Default::default() }
}
}

View File

@ -143,6 +143,33 @@ impl Launcher {
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");
@ -154,7 +181,15 @@ impl Launcher {
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());
@ -204,7 +239,16 @@ impl Launcher {
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"]);
cmd.spawn();
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);
}
}*/
}
}

View File

@ -1,8 +1,11 @@
use std::sync::{Arc, Mutex};
use base64::prelude::{BASE64_STANDARD, BASE64_STANDARD_NO_PAD};
use base64::Engine;
use config::LauncherConfig;
use launcher::Launcher;
use serde::{Deserialize, Serialize};
use tokio::process::Command;
use tokio::runtime::Runtime;
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
use winit::application::ApplicationHandler;
@ -196,7 +199,20 @@ async fn main() {
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["add_server_response".to_string(), status.to_string(), msg.to_string()] }).unwrap()));
}
"fetch_settings" => {
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["fetch_settings_response".to_string(), launcher.config.show_alpha.to_string(), launcher.config.show_beta.to_string(), launcher.config.show_snapshots.to_string(), launcher.config.java_path.clone(), launcher.config.ram_amount.to_string()] }).unwrap()));
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["fetch_settings_response".to_string(), launcher.config.show_alpha.to_string(), launcher.config.show_beta.to_string(), launcher.config.show_snapshots.to_string(), launcher.config.java_path.clone(), launcher.config.ram_amount.to_string(), launcher.config.enable_blur.to_string()] }).unwrap()));
}
"save_bg" => {
let params = &params.unwrap().params;
let mut p = launcher.config.launcher_dir();
p.push("bg.base64");
let _ = std::fs::write(p, &params[0]);
}
"fetch_bg" => {
let mut p = launcher.config.launcher_dir();
p.push("bg.base64");
if let Ok(data) = std::fs::read(p) {
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["fetch_bg".to_string(), String::from_utf8(data).unwrap()] }).unwrap()));
}
}
"update_settings" => {
let params = &params.unwrap().params;
@ -205,8 +221,22 @@ async fn main() {
launcher.config.show_alpha = params[0].parse().unwrap();
launcher.config.show_beta = params[1].parse().unwrap();
launcher.config.show_snapshots = params[2].parse().unwrap();
launcher.config.enable_blur = params[5].parse().unwrap();
launcher.save_config();
}
"open_file" => {
let params = &params.unwrap().params;
let _ = Command::new("cmd").args(["/C", "start", &params[0]]).spawn();
}
"load_screenshots" => {
let screenshots = launcher.get_screenshots();
let mut svec = Vec::new();
for (path,data) in screenshots {
svec.push(path);
svec.push(data);
}
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: [vec!["load_screenshots".to_string()], svec].concat() }).unwrap()));
}
_ => {}
}
}

View File

@ -92,7 +92,7 @@
<li>
<a
href="#"
onclick="showSection(this, 'screenshots')"
onclick="showSection(this, 'screenshots'); loadScreenshots()"
class="menu-btn group relative flex justify-center rounded-sm px-2 py-1.5 text-gray-500 hover:bg-gray-50 hover:text-gray-700"
>
<i class="fa-solid fa-image"></i>
@ -128,287 +128,286 @@
<div class="flex flex-col justify-center items-center">
<div id="popup" role="alert" class="mt-5 rounded-xl border border-gray-100 bg-white p-4 w-96 hidden">
<div class="flex items-start gap-4">
<span class="text-green-600">
<i class="fa-solid fa-info"></i>
</span>
<div class="flex-1">
<p id="popup_text" class="mt-1 text-sm text-gray-700">Your product changes have been saved.</p>
</div>
<button onClick="dismissPopup()" class="text-gray-500 transition hover:text-gray-600">
<span class="sr-only">Dismiss popup</span>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-6"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
<div id="popup" role="alert" class="mt-5 rounded-xl border border-gray-100 bg-white p-4 w-96 hidden">
<div class="flex items-start gap-4">
<span class="text-green-600">
<i class="fa-solid fa-info"></i>
</span>
<div class="flex justify-center items-center w-screen h-screen">
<div id="logs-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">Minecraft Logs</h2>
<!-- Log Output -->
<div id="log-container" class="mt-4 p-2 h-48 overflow-auto bg-gray-200 rounded text-left text-sm text-gray-800 font-mono">
<p>[INFO] Initializing Minecraft...</p>
<div class="flex-1">
<p id="popup_text" class="mt-1 text-sm text-gray-700">Your product changes have been saved.</p>
</div>
<button onClick="dismissPopup()" class="text-gray-500 transition hover:text-gray-600">
<span class="sr-only">Dismiss popup</span>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-6"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<!-- Stop & Back Buttons -->
<div class="mt-4 flex gap-2">
<button onclick="stopMinecraft()"
class="w-full bg-red-500 hover:bg-red-600 text-white font-bold py-2 px-4 rounded transition">
Kill
</button>
<button
class="w-full bg-gray-500 hover:bg-gray-600 text-white font-bold py-2 px-4 rounded transition">
Copy logs
</button>
</div>
<p class="mt-4 text-sm text-gray-500">alterdekim</p>
</div>
<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>
<div class="flex justify-center items-center w-screen h-screen">
<div id="logs-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">Minecraft Logs</h2>
<!-- Loading Status -->
<p id="loading-text" class="mt-2 text-gray-500">Preparing...</p>
<!-- Progress Bar -->
<div class="mt-4 w-full bg-gray-200 rounded-full">
<div id="progress-bar" class="h-4 bg-green-500 rounded-full transition-all" style="width: 0%;"></div>
<!-- Log Output -->
<div id="log-container" class="mt-4 p-2 h-48 overflow-auto bg-gray-200 rounded text-left text-sm text-gray-800 font-mono">
<p>[INFO] Initializing Minecraft...</p>
</div>
<p class="mt-4 text-sm text-gray-500">alterdekim</p>
</div>
<div id="installation-section" class="xsection bg-white shadow-lg rounded-xl p-6 w-96 text-center">
<h1 class="text-2xl font-semibold text-gray-700">Welcome to XCraft</h1>
<p class="mt-2 text-gray-600">Choose how you want to set up Minecraft.</p>
<!-- Portable Mode -->
<button onclick="runPortable()"
class="mt-6 w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded transition">
Run in Portable Mode
</button>
<p class="text-sm text-gray-500 mt-1">No installation required. Saves everything in a local folder.</p>
<!-- Installation Mode -->
<button onclick="installMinecraft()"
class="mt-4 w-full bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition">
Install Minecraft
</button>
<p class="text-sm text-gray-500 mt-1">Installs Minecraft on your system for long-term use.</p>
</div>
<div id="login-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">CraftX v1.0</h2>
<!-- Username -->
<input id="sign_up_username" type="text" placeholder="Username" class="mt-4 w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500"> <!-- bg-red-50 border border-red-500 text-red-900 placeholder-red-700 focus:ring-2 focus:ring-red-500 -->
<p class="mt-2 text-sm text-red-600 dark:text-red-500 text-left hidden">Username has invalid characters or too short</p>
<!-- Sign In Button -->
<button
onclick="signUp()"
class="mt-4 w-full bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition">
Sign Up
</button>
<!-- Footer -->
<p class="mt-4 text-sm text-gray-500">alterdekim</p>
</div>
<div id="add-section" class="xsection bg-white shadow-lg rounded-xl p-6 w-96 text-center hidden">
<h1 class="text-2xl font-semibold text-gray-700">New instance</h1>
<!-- Version Selection -->
<label class="block mt-4 text-gray-600">Select Version:</label>
<select id="mc-version" class="mt-2 w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500">
</select>
<!-- Download Button -->
<button
onclick="downloadSelectedVersion()"
class="mt-4 w-full bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition">
<i class="fa-solid fa-download"></i> Download Version
</button>
<!-- OR Divider -->
<div class="mt-4 text-gray-500 text-sm">or</div>
<!-- Add MultiMC Instance -->
<button
class="mt-3 w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded transition">
<i class="fa-solid fa-file-zipper"></i> Add MultiMC Instance
</button>
<p class="mt-4 text-sm text-gray-500">alterdekim</p>
</div>
<div id="settings-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">XCraft Settings</h2>
<div class="mt-4 flex justify-around text-gray-600">
<button onClick="setSettingsTab('launcher')" class="py-2 px-4 focus:text-green-500"><i class="fa-solid fa-rocket"></i></br>Launcher</button>
<button onClick="setSettingsTab('appearance')" class="py-2 px-4 focus:text-green-500"><i class="fa-solid fa-palette"></i></br>Appearance</button>
<button onClick="setSettingsTab('java')" class="py-2 px-4 focus:text-green-500"><i class="fa-solid fa-mug-hot"></i></br>Java</button>
</div>
<div id="launcher-settings" class="settings-tab mt-4">
<div class="mt-4 flex justify-between items-center">
<label class="inline-flex items-center cursor-pointer">
<input id="show-beta" type="checkbox" value="" class="sr-only peer">
<div class="relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 dark:peer-focus:ring-green-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-green-600 dark:peer-checked:bg-green-600"></div>
<span class="ms-3 text-sm font-medium text-gray-900 dark:text-gray-300">Show beta versions</span>
</label>
</div>
<div class="mt-4 flex justify-between items-center">
<label class="inline-flex items-center cursor-pointer">
<input id="show-alpha" type="checkbox" value="" class="sr-only peer">
<div class="relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 dark:peer-focus:ring-green-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-green-600 dark:peer-checked:bg-green-600"></div>
<span class="ms-3 text-sm font-medium text-gray-900 dark:text-gray-300">Show alpha versions</span>
</label>
</div>
<div class="mt-4 flex justify-between items-center">
<label class="inline-flex items-center cursor-pointer">
<input id="show-snapshots" type="checkbox" value="" class="sr-only peer">
<div class="relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 dark:peer-focus:ring-green-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-green-600 dark:peer-checked:bg-green-600"></div>
<span class="ms-3 text-sm font-medium text-gray-900 dark:text-gray-300">Show snapshots</span>
</label>
</div>
</div>
<div id="appearance-settings" class="settings-tab mt-4 hidden">
<div class="mt-4 flex items-center border border-gray-300 rounded-lg overflow-hidden">
<input type="file" id="bg-picker" class="hidden" accept=".jpg, .jpeg, .png" onchange="bgSelected(event)">
<input id="background-path" type="text" placeholder="Custom background name"
class="flex-1 px-4 py-2 focus:outline-none">
<button onclick="$('#bg-picker').click()"
class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 font-bold transition">
Set
<!-- Stop & Back Buttons -->
<div class="mt-4 flex gap-2">
<button
class="w-full bg-gray-500 hover:bg-gray-600 text-white font-bold py-2 px-4 rounded transition">
Copy logs
</button>
</div>
</div>
<div id="java-settings" class="settings-tab mt-4 hidden">
<div class="mt-4 flex items-center border border-gray-300 rounded-lg overflow-hidden">
<input id="java-path" type="text" placeholder="Enter Java Path"
class="flex-1 px-4 py-2 focus:outline-none">
<button onclick="locateJava()"
class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 font-bold transition">
Auto
</button>
</div>
<label for="ram-input" class="block mb-2 text-sm font-medium text-gray-800 dark:text-white">Allocate memory (MB):</label>
<input type="number" id="ram-input" min="1024" step="1" aria-describedby="helper-text-explanation" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-green-500 focus:border-green-500 block w-full p-2.5" placeholder="1024" value="1024" required />
</div>
<button
onClick="saveSettings()" class="w-full mt-6 bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition">
<i class="fa-solid fa-floppy-disk"></i> Save
</button>
<p class="mt-4 text-sm text-gray-500">alterdekim</p>
</div>
<div id="appearance-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">Account Settings</h2>
</div>
<div class="mt-6 text-left">
<label class="text-gray-600 font-medium">Offline username</label>
<input type="text" placeholder="Username" class="mt-4 w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500">
</div>
<!-- Skin Upload -->
<div class="mt-6 text-left">
<label class="text-gray-600 font-medium">Minecraft Skin</label>
<div class="mt-2 flex items-center gap-4">
<img id="skin-preview" src="https://www.mc-heads.net/avatar/notch" class="w-16 h-16 rounded-md border" alt="Skin Preview">
<input id="skin-upload" type="file" accept=".png" class="hidden" onchange="previewSkin()">
<button onclick="document.getElementById('skin-upload').click()"
class="bg-green-500 hover:bg-green-700 text-white px-4 py-2 rounded">
Upload
</button>
<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>
<p id="loading-text" class="mt-2 text-gray-500">Preparing...</p>
<div class="mt-4 w-full bg-gray-200 rounded-full">
<div id="progress-bar" class="h-4 bg-green-500 rounded-full transition-all" style="width: 0%;"></div>
</div>
<p class="mt-4 text-sm text-gray-500">alterdekim</p>
</div>
<!-- Cape Upload -->
<div class="mt-6 text-left">
<label class="text-gray-600 font-medium">Minecraft Cape</label>
<div class="mt-2 flex items-center gap-4">
<img id="cape-preview" src="https://skinmc.net/capes/77863/download" class="w-16 h-16 rounded-md border" alt="Cape Preview">
<input id="cape-upload" type="file" accept=".png" class="hidden" onchange="previewCape()">
<button onclick="document.getElementById('cape-upload').click()"
class="bg-green-500 hover:bg-green-700 text-white px-4 py-2 rounded">
Upload
</button>
<div id="installation-section" class="xsection bg-white shadow-lg rounded-xl p-6 w-96 text-center">
<h1 class="text-2xl font-semibold text-gray-700">Welcome to XCraft</h1>
<p class="mt-2 text-gray-600">Choose how you want to set up Minecraft.</p>
<button onclick="runPortable()"
class="mt-6 w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded transition">
Run in Portable Mode
</button>
<p class="text-sm text-gray-500 mt-1">No installation required. Saves everything in a local folder.</p>
<button onclick="installMinecraft()"
class="mt-4 w-full bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition">
Install Minecraft
</button>
<p class="text-sm text-gray-500 mt-1">Installs Minecraft on your system for long-term use.</p>
</div>
<div id="login-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">CraftX v1.0</h2>
<input id="sign_up_username" type="text" placeholder="Username" class="mt-4 w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500"> <!-- bg-red-50 border border-red-500 text-red-900 placeholder-red-700 focus:ring-2 focus:ring-red-500 -->
<p class="mt-2 text-sm text-red-600 dark:text-red-500 text-left hidden">Username has invalid characters or too short</p>
<button
onclick="signUp()"
class="mt-4 w-full bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition">
Sign Up
</button>
<p class="mt-4 text-sm text-gray-500">alterdekim</p>
</div>
<div id="add-section" class="xsection bg-white shadow-lg rounded-xl p-6 w-96 text-center hidden">
<h1 class="text-2xl font-semibold text-gray-700">New instance</h1>
<label class="block mt-4 text-gray-600">Select Version:</label>
<select id="mc-version" class="mt-2 w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500">
</select>
<button
onclick="downloadSelectedVersion()"
class="mt-4 w-full bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition">
<i class="fa-solid fa-download"></i> Download Version
</button>
<div class="mt-4 text-gray-500 text-sm">or</div>
<button
class="mt-3 w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded transition">
<i class="fa-solid fa-file-zipper"></i> Add MultiMC Instance
</button>
<p class="mt-4 text-sm text-gray-500">alterdekim</p>
</div>
<div id="settings-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">XCraft Settings</h2>
<div class="mt-4 flex justify-around text-gray-600">
<button onClick="setSettingsTab('launcher')" class="py-2 px-4 focus:text-green-500"><i class="fa-solid fa-rocket"></i></br>Launcher</button>
<button onClick="setSettingsTab('appearance')" class="py-2 px-4 focus:text-green-500"><i class="fa-solid fa-palette"></i></br>Appearance</button>
<button onClick="setSettingsTab('java')" class="py-2 px-4 focus:text-green-500"><i class="fa-solid fa-mug-hot"></i></br>Java</button>
</div>
<div id="launcher-settings" class="settings-tab mt-4">
<div class="mt-4 flex justify-between items-center">
<label class="inline-flex items-center cursor-pointer">
<input id="show-beta" type="checkbox" value="" class="sr-only peer">
<div class="relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 dark:peer-focus:ring-green-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-green-600 dark:peer-checked:bg-green-600"></div>
<span class="ms-3 text-sm font-medium text-gray-900 dark:text-gray-300">Show beta versions</span>
</label>
</div>
<div class="mt-4 flex justify-between items-center">
<label class="inline-flex items-center cursor-pointer">
<input id="show-alpha" type="checkbox" value="" class="sr-only peer">
<div class="relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 dark:peer-focus:ring-green-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-green-600 dark:peer-checked:bg-green-600"></div>
<span class="ms-3 text-sm font-medium text-gray-900 dark:text-gray-300">Show alpha versions</span>
</label>
</div>
<div class="mt-4 flex justify-between items-center">
<label class="inline-flex items-center cursor-pointer">
<input id="show-snapshots" type="checkbox" value="" class="sr-only peer">
<div class="relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 dark:peer-focus:ring-green-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-green-600 dark:peer-checked:bg-green-600"></div>
<span class="ms-3 text-sm font-medium text-gray-900 dark:text-gray-300">Show snapshots</span>
</label>
</div>
</div>
</div>
<!-- Save Button -->
<button
class="mt-6 w-full bg-green-500 hover:bg-green-700 text-white py-2 rounded font-bold transition">
Save Settings
</button>
</div>
<div id="accounts-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">
<img src="https://www.mc-heads.net/avatar/MHF_Steve" class="w-12 h-12 rounded-full">
<div class="h-fill ms-2">
<h2 class="text-lg font-semibold">DartJevder</h2>
<h2 class="text-sm font-semibold">Blatnoe Pivo</h2>
<div id="appearance-settings" class="settings-tab mt-4 hidden">
<div class="mt-4 flex items-center border border-gray-300 rounded-lg overflow-hidden">
<input type="file" id="bg-picker" class="hidden" accept=".jpg, .jpeg, .png" onchange="bgSelected(event)">
<input id="background-path" type="text" placeholder="Custom background name"
class="flex-1 px-4 py-2 focus:outline-none">
<button onclick="$('#bg-picker').click()"
class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 font-bold transition">
Set
</button>
</div>
<div class="mt-4 flex justify-between items-center">
<label class="inline-flex items-center cursor-pointer">
<input id="enable-blur" type="checkbox" value="" class="sr-only peer">
<div class="relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 dark:peer-focus:ring-green-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-green-600 dark:peer-checked:bg-green-600"></div>
<span class="ms-3 text-sm font-medium text-gray-900 dark:text-gray-300">Enable blur</span>
</label>
</div>
</div>
<div id="java-settings" class="settings-tab mt-4 hidden">
<div class="mt-4 flex items-center border border-gray-300 rounded-lg overflow-hidden">
<input id="java-path" type="text" placeholder="Enter Java Path"
class="flex-1 px-4 py-2 focus:outline-none">
<button onclick="locateJava()"
class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 font-bold transition">
Auto
</button>
</div>
<label for="ram-input" class="block mb-2 text-sm font-medium text-gray-800 dark:text-white">Allocate memory (MB):</label>
<input type="number" id="ram-input" min="1024" step="1" aria-describedby="helper-text-explanation" class="bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-green-500 focus:border-green-500 block w-full p-2.5" placeholder="1024" value="1024" required />
</div>
<button
onClick="saveSettings()" class="w-full mt-6 bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition">
<i class="fa-solid fa-floppy-disk"></i> Save
</button>
<p class="mt-4 text-sm text-gray-500">alterdekim</p>
</div>
<div id="appearance-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">Account Settings</h2>
<div class="mt-6 text-left">
<label class="text-gray-600 font-medium">Offline username</label>
<input type="text" placeholder="Username" class="mt-4 w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500">
</div>
<!-- Skin Upload -->
<div class="mt-6 text-left">
<label class="text-gray-600 font-medium">Minecraft Skin</label>
<div class="mt-2 flex items-center gap-4">
<img id="skin-preview" src="https://www.mc-heads.net/avatar/notch" class="w-16 h-16 rounded-md border" alt="Skin Preview">
<input id="skin-upload" type="file" accept=".png" class="hidden" onchange="previewSkin()">
<button onclick="document.getElementById('skin-upload').click()"
class="bg-green-500 hover:bg-green-700 text-white px-4 py-2 rounded">
Upload
</button>
</div>
</div>
</div>
</div>
<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">
<img src="" class="w-12 h-12 rounded-full">
<div class="h-fill ms-2">
<h2 class="text-lg font-semibold">Release</h2>
<h2 class="text-sm font-semibold">1.12.2</h2>
<!-- Cape Upload -->
<div class="mt-6 text-left">
<label class="text-gray-600 font-medium">Minecraft Cape</label>
<div class="mt-2 flex items-center gap-4">
<img id="cape-preview" src="https://skinmc.net/capes/77863/download" class="w-16 h-16 rounded-md border" alt="Cape Preview">
<input id="cape-upload" type="file" accept=".png" class="hidden" onchange="previewCape()">
<button onclick="document.getElementById('cape-upload').click()"
class="bg-green-500 hover:bg-green-700 text-white px-4 py-2 rounded">
Upload
</button>
</div>
</div>
</div>
</div>
<div id="add-server-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">Add Server</h2>
<input id="server_address" type="text" placeholder="127.0.0.1:25565#8999" class="mt-4 w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500">
<p class="mt-2 text-sm text-green-500 text-left">XCraft servers are using their own session servers, to specify its port, use #PORT, or don't if it's 8999</p>
<input id="server_username" type="text" placeholder="Username" class="mt-4 w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500"> <!-- bg-red-50 border border-red-500 text-red-900 placeholder-red-700 focus:ring-2 focus:ring-red-500 -->
<p class="mt-2 text-sm text-red-600 dark:text-red-500 text-left hidden">Username has invalid characters or too short</p>
<input id="server_password" type="password" placeholder="Password" class="mt-4 w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500">
<!-- Sign In Button -->
<!-- Save Button -->
<button
onclick="addServerInstance()"
class="mt-4 w-full bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition">
Add
class="mt-6 w-full bg-green-500 hover:bg-green-700 text-white py-2 rounded font-bold transition">
Save Settings
</button>
</div>
<!-- Footer -->
<p class="mt-4 text-sm text-gray-500">alterdekim</p>
</div>
<div id="accounts-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">
<img src="https://www.mc-heads.net/avatar/MHF_Steve" class="w-12 h-12 rounded-full">
<div class="h-fill ms-2">
<h2 class="text-lg font-semibold">DartJevder</h2>
<h2 class="text-sm font-semibold">Blatnoe Pivo</h2>
</div>
</div>
</div>
<div id="servers-section" class="xsection grid grid-cols-3 gap-4 p-6 w-fill hidden">
<div onClick="addServer()" class="bg-white shadow-lg rounded-xl w-48 h-24 flex justify-center items-center text-3xl text-green-500 cursor-pointer hover:bg-green-500 hover:text-white"><i class="fa-solid fa-plus"></i></div>
<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">
<img src="" class="w-12 h-12 rounded-full">
<div class="h-fill ms-2">
<h2 class="text-lg font-semibold">Release</h2>
<h2 class="text-sm font-semibold">1.12.2</h2>
</div>
</div>
</div>
<div id="screenshots-section" class="xsection grid grid-cols-3 gap-4 p-6 w-fill h-screen overflow-y-auto hidden">
</div>
<div id="add-server-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">Add Server</h2>
<input id="server_address" type="text" placeholder="127.0.0.1:25565#8999" class="mt-4 w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500">
<p class="mt-2 text-sm text-green-500 text-left">XCraft servers are using their own session servers, to specify its port, use #PORT, or don't if it's 8999</p>
<input id="server_username" type="text" placeholder="Username" class="mt-4 w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500"> <!-- bg-red-50 border border-red-500 text-red-900 placeholder-red-700 focus:ring-2 focus:ring-red-500 -->
<p class="mt-2 text-sm text-red-600 dark:text-red-500 text-left hidden">Username has invalid characters or too short</p>
<input id="server_password" type="password" placeholder="Password" class="mt-4 w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500">
<!-- Sign In Button -->
<button
onclick="addServerInstance()"
class="mt-4 w-full bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition">
Add
</button>
<!-- Footer -->
<p class="mt-4 text-sm text-gray-500">alterdekim</p>
</div>
<div id="servers-section" class="xsection grid grid-cols-3 gap-4 p-6 w-fill hidden">
<div onClick="addServer()" class="bg-white shadow-lg rounded-xl w-48 h-24 flex justify-center items-center text-3xl text-green-500 cursor-pointer hover:bg-green-500 hover:text-white"><i class="fa-solid fa-plus"></i></div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
@ -449,16 +448,54 @@
} else if( params[i] == "fetch_settings_response" ) {
setSettings(params.slice(i+1));
break;
} else if( params[i] == "load_screenshots" ) {
setScreenshots(params.slice(i+1));
break;
} else if( params[i] == "fetch_bg") {
setBackground(params[i+1]);
i++;
}
}
}
function setBackground(bg) {
$("body").removeClass("bg-gray-100");
$("body").css("background-image", 'url("'+bg+'")');
$("body").css("background-size", "cover");
$("body").css("background-repeat", "no-repeat");
$("body").css("background-position", "center center");
}
function setScreenshots(params) {
$("#screenshots-section").html("");
for( let i = 0; i < params.length; i+=2 ) {
$("#screenshots-section").append(
`<div onclick="openFile('`+params[i].replaceAll("\\", "\\\\")+`')" class="bg-white cursor-pointer hover:bg-green-500 hover:text-white shadow-lg rounded-xl w-48 h-[7rem] flex justify-center items-center">
<img src="`+params[i+1]+`" class="w-fill h-fill rounded-xl">
</div>`
);
}
}
function openFile(path) {
$.post({url: "open_file", data: JSON.stringify({ params: [path] })}, processParams);
}
function loadScreenshots() {
$.post({url: "load_screenshots" }, processParams);
}
function bgSelected(event) {
let file = event.target.files[0];
if(file) {
let allowedTypes = ["image/jpeg", "image/png"];
if (allowedTypes.includes(file.type)) {
$("#background-path").val(file.name);
let reader = new FileReader();
reader.onload = function(e) {
$.post({url: "save_bg", data: JSON.stringify({ params: [e.target.result] })}, processParams);
}
reader.readAsDataURL(file);
} else {
showPopup("This format is not allowed!");
}
@ -471,7 +508,8 @@
let show_snapshots = $("#show-snapshots").prop('checked');
let java_path = $("#java-path").val();
let ram_input = $("#ram-input").val();
$.post({url: "update_settings", data: JSON.stringify({ params: [show_alpha+"", show_beta+"", show_snapshots+"", java_path, ram_input+""] }) }, processParams);
let enable_blur = $("#enable-blur").prop('checked');
$.post({url: "update_settings", data: JSON.stringify({ params: [show_alpha+"", show_beta+"", show_snapshots+"", java_path, ram_input+"", enable_blur+""] }) }, processParams);
showPopup("Saved!");
}
@ -481,6 +519,17 @@
$("#show-snapshots").prop('checked', (params[2] === 'true'));
$("#java-path").val(params[3]);
$("#ram-input").val(params[4]);
$("#enable-blur").prop('checked', (params[5] == 'true'));
if( params[5] == 'true' ) {
enableBlur();
}
}
function enableBlur() {
$(".xsection").removeClass("bg-white");
$(".xsection").addClass("backdrop-blur-md");
$("#sidebar").removeClass("bg-white");
$("#sidebar").addClass("backdrop-blur-md");
}
function dismissPopup() {
@ -567,6 +616,7 @@
function runInstance(version) {
$.post({url: "run_instance", data: JSON.stringify({ params: [version] })}, processParams);
showSection(undefined, "logs");
}
function downloadSelectedVersion() {
@ -623,6 +673,7 @@
$( document ).ready(async function() {
$.get("check_installation", processParams);
$.get("fetch_settings", processParams);
$.get("fetch_bg", processParams);
setInterval(function() {
if( !$("#loading-section").hasClass("hidden") ) {
$.get("check_download_status", processParams);

View File

@ -1,183 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minecraft Launcher - Login</title>
<script src="https://code.jquery.com/jquery-3.7.1.slim.min.js" integrity="sha256-kmHvs0B+OpCW5GVHUNjv9rOmY0IvSIRcf7zGUDTDQM8=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" />
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100 flex justify-center items-center h-screen">
<!-- Container -->
<div id="login-screen" class="bg-white shadow-lg rounded-xl p-6 w-96 text-center">
<h2 class="text-2xl font-semibold text-gray-700">CraftX v1.0</h2>
<!-- Username -->
<input type="text" placeholder="Username" class="mt-4 w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500">
<!-- Password -->
<!-- <input type="password" placeholder="Password" class="mt-3 w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500"> -->
<!-- Sign In Button -->
<button onclick="showAccountSettings()"
class="mt-4 w-full bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition">
Sign In
</button>
<!-- Footer -->
<p class="mt-4 text-sm text-gray-500">alterdekim</p>
</div>
<div id="main-screen" class="bg-white shadow-lg rounded-xl p-6 w-96 text-center hidden">
<h1 class="text-2xl font-semibold text-gray-700">Minecraft Launcher</h1>
<!-- Version Selection -->
<label class="block mt-4 text-gray-600">Select Version:</label>
<select id="mc-version" class="mt-2 w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500">
<option value="latest">Latest Release</option>
<option value="1.20.1">1.20.1</option>
<option value="1.19.4">1.19.4</option>
<option value="1.18.2">1.18.2</option>
<option value="1.16.5">1.16.5</option>
<option value="1.12.2">1.12.2 (Modding)</option>
</select>
<!-- Download Button -->
<button onclick="downloadVersion()"
class="mt-4 w-full bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition">
<i class="fa-solid fa-download"></i> Download Version
</button>
<!-- OR Divider -->
<div class="mt-4 text-gray-500 text-sm">or</div>
<!-- Add MultiMC Instance -->
<button onclick="addMultiMCInstance()"
class="mt-3 w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded transition">
<i class="fa-solid fa-file-zipper"></i> Add MultiMC Instance
</button>
<!-- Navigation Buttons -->
<div class="mt-6 flex gap-2">
<button onclick="goToSettings()"
class="w-1/2 bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded transition">
<i class="fa-solid fa-gear"></i> Settings
</button>
<button onclick="showLoginScreen()"
class="w-1/2 bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded transition">
<i class="fa-solid fa-key"></i> Logout
</button>
</div>
<p class="mt-4 text-sm text-gray-500">alterdekim</p>
</div>
<div id="launcher-settings" class="ms-10 bg-white shadow-lg rounded-xl p-6 w-96 text-center hidden">
<h2 class="text-2xl font-semibold text-gray-700">Launcher Settings</h2>
<!-- Tabs -->
<div class="mt-4 flex justify-around text-gray-600">
<button onclick="showSection('launcher')" class="py-2 px-4 focus:text-green-500"><i class="fa-solid fa-rocket"></i> Launcher</button>
<button onclick="showSection('minecraft')" class="py-2 px-4 focus:text-green-500"><i class="fa-solid fa-gamepad"></i> Minecraft</button>
<button onclick="showSection('java')" class="py-2 px-4 focus:text-green-500"><i class="fa-solid fa-mug-hot"></i> Java</button>
<button onclick="showSection('versions')" class="py-2 px-4 focus:text-green-500"><i class="fa-solid fa-cube"></i> Versions</button>
</div>
<!-- Sections -->
<div id="launcher" class="settings-tab mt-4">
<h3 class="text-lg font-semibold text-gray-700"><i class="fa-solid fa-rocket"></i> Launcher Settings</h3>
<label class="block mt-3 text-gray-600">Auto-update:</label>
<select class="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-green-500">
<option>Enabled</option>
<option>Disabled</option>
</select>
<label class="block mt-3 text-gray-600">UI Theme:</label>
<select class="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-green-500">
<option>Light</option>
<option>Dark</option>
</select>
</div>
<div id="minecraft" class="settings-tab mt-4 hidden">
<h3 class="text-lg font-semibold text-gray-700"><i class="fa-solid fa-gamepad"></i> Minecraft Settings</h3>
<label class="block mt-3 text-gray-600">Game Directory:</label>
<input type="text" placeholder="/path/to/minecraft" class="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-green-500">
<label class="block mt-3 text-gray-600">RAM Allocation:</label>
<input type="range" min="1024" max="16384" step="512" class="w-full">
<label class="block mt-3 text-gray-600">Enable Mods:</label>
<input type="checkbox" class="ml-2">
</div>
<div id="java" class="settings-tab mt-4 hidden">
<h3 class="text-lg font-semibold text-gray-700"><i class="fa-solid fa-mug-hot"></i> Java Settings</h3>
<label class="block mt-3 text-gray-600">Java Path:</label>
<input type="text" placeholder="/path/to/java" class="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-green-500">
<label class="block mt-3 text-gray-600">Custom JVM Arguments:</label>
<textarea class="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-green-500" rows="2">-Xmx4G -Xms2G</textarea>
</div>
<div id="versions" class="settings-tab mt-4 hidden">
<h3 class="text-lg font-semibold text-gray-700"><i class="fa-solid fa-cube"></i> Version Settings</h3>
<label class="block mt-3 text-gray-600">Show Experimental Versions:</label>
<input type="checkbox" class="ml-2">
<label class="block mt-3 text-gray-600">Show Snapshots:</label>
<input type="checkbox" class="ml-2">
</div>
<!-- Save & Back Buttons -->
<div class="mt-6 flex gap-2">
<button onclick="saveSettings()"
class="w-1/2 bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition">
💾 Save
</button>
<button onclick="goBack()"
class="w-1/2 bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded transition">
⬅ Back
</button>
</div>
<p class="mt-4 text-sm text-gray-500">Minecraft Launcher v1.0</p>
</div>
<script>
function showSection(section) {
$(".settings-tab").each(function(i, d) {
$(d).addClass('hidden');
});
$("#"+section).removeClass('hidden');
}
function goBack() {
$("#launcher-settings").addClass('hidden');
}
function goToSettings() {
$("#launcher-settings").removeClass('hidden');
}
function downloadVersion() {
window.ipc.postMessage("show_play_screen");
}
function showAccountSettings() {
$("#main-screen").removeClass('hidden');
$("#login-screen").addClass('hidden');
}
function showLoginScreen() {
$("#main-screen").addClass('hidden');
$("#login-screen").removeClass('hidden');
}
</script>
</body>
</html>