Compare commits

..

No commits in common. "master" and "0.1.0" have entirely different histories.

11 changed files with 65 additions and 237 deletions

2
Cargo.lock generated
View File

@ -4,7 +4,7 @@ version = 4
[[package]]
name = "CraftX"
version = "0.1.3"
version = "0.1.0"
dependencies = [
"base64 0.22.1",
"dirs",

View File

@ -1,6 +1,6 @@
[package]
name = "CraftX"
version = "0.1.3"
version = "0.1.0"
edition = "2024"
build = "build.rs"

View File

@ -1,18 +1,4 @@
<img align="left" width="100" height="100" src="https://w0n.zip/file/bWJVJa">
### XCraft
A Modern Minecraft Launcher
#
<div align="center">
<img src="https://w0n.zip/file/Xe0zXb">
<p>
<small>
Screenshot from Windows 10
</small>
</p>
</div>
# XCraft - A Modern Minecraft Launcher
XCraft is a custom Minecraft launcher written in Rust, designed to provide enhanced flexibility and customization. It supports a custom online mode, skin and cape editing, and seamless integration with MultiMC instances.
@ -27,8 +13,8 @@ XCraft is a custom Minecraft launcher written in Rust, designed to provide enhan
## Installation
### Download Stable Build
You can download the latest stable build of XCraft from releases section:
[Download from Gitea](https://gitea.awain.net/alterwain/XCraft/releases/latest).
You can download the latest stable build of XCraft from our Jenkins CI server:
[Download from Jenkins](https://jenkins.awain.net/job/XCraft/lastStableBuild/).
### Build from Source
@ -56,7 +42,6 @@ To make your Minecraft server compatible with XCraft, install the [**XCraftAuth*
## Roadmap
- [ ] Cross-platform support improvements
- [ ] Fabric integration
## License
This project is licensed under the MIT License.

2
src/js/jquery.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -301,7 +301,7 @@ impl Launcher {
if let Ok(data) = std::fs::read(&instances) {
let config: VersionConfig = serde_json::from_slice(&data).unwrap();
minecraft_arguments = Some(config.minecraft_arguments);
minecraft_arguments = Some(config.minecraftArguments);
let mut libraries_cmd = Vec::new();
for library in config.libraries {
if let Some(classifier) = &library.downloads.classifiers {
@ -330,7 +330,7 @@ impl Launcher {
}
libraries_cmd.push(client_jar.to_str().unwrap().to_string());
cmd.arg(libraries_cmd.concat());
cmd.arg(config.main_class.clone());
cmd.arg(config.mainClass.clone());
let mut game_dir = self.config.instances_path();
game_dir.push(&instance_name);
@ -346,7 +346,7 @@ impl Launcher {
"${version_name}" => &instance_name,
"${game_directory}" => game_dir.to_str().unwrap(),
"${assets_root}" => assets_dir.to_str().unwrap(),
"${assets_index_name}" => &config.asset_index.as_ref().unwrap().id,
"${assets_index_name}" => &config.assetIndex.as_ref().unwrap().id,
"${auth_uuid}" => &uuid,
"${auth_access_token}" => &token,
"${user_properties}" => "{}",
@ -423,8 +423,8 @@ impl Launcher {
let mut minecraft_config = None;
let mut forge_version = None;
for component in pack_mmc.components.iter().filter(|c| c.cached_name.is_some()) {
match component.cached_name.as_ref().unwrap().as_str() {
for component in pack_mmc.components.iter().filter(|c| c.cachedName.is_some()) {
match component.cachedName.as_ref().unwrap().as_str() {
"Minecraft" => minecraft_config = Some(crate::minecraft::versions::find_version_object(&component.version).await?),
"Forge" => {
forge_version = Some(component.version.clone());
@ -502,15 +502,15 @@ impl Launcher {
let _ = std::fs::create_dir_all(objects);
let mut index = assets_path.clone();
index.push(config.asset_index.as_ref().unwrap().to_path());
index.push(config.assetIndex.as_ref().unwrap().to_path());
let _ = util::download_file(&config.asset_index.as_ref().unwrap().url, index.to_str().unwrap(), sx.clone(), "Downloading assets indexes", false).await;
let _ = util::download_file(&config.assetIndex.as_ref().unwrap().url, index.to_str().unwrap(), sx.clone(), "Downloading assets indexes", false).await;
cnt += 1;
let asset_index = config.asset_index.as_ref().unwrap().url.clone();
let asset_index = config.assetIndex.as_ref().unwrap().url.clone();
overall_size += config.asset_index.as_ref().unwrap().size as usize;
overall_size += config.asset_index.as_ref().unwrap().total_size as usize;
overall_size += config.assetIndex.as_ref().unwrap().size as usize;
overall_size += config.assetIndex.as_ref().unwrap().totalSize as usize;
let assets = crate::minecraft::assets::fetch_assets_list(&asset_index).await.unwrap().objects;
@ -574,8 +574,8 @@ impl Launcher {
let version_json: VersionConfig = serde_json::from_slice(&version_json)?;
let mut edited = minecraft_config.clone().unwrap();
edited.main_class = version_json.main_class.clone();
edited.minecraft_arguments = version_json.minecraft_arguments;
edited.mainClass = version_json.mainClass.clone();
edited.minecraftArguments = version_json.minecraftArguments;
edited.libraries.retain(|l| !version_json.libraries.iter().any(|t| t.name == l.name));
for i in 0..version_json.libraries.len() {
edited.libraries.push(version_json.libraries[i].clone());
@ -706,15 +706,15 @@ impl Launcher {
let _ = std::fs::create_dir_all(objects);
let mut index = assets_path.clone();
index.push(config.asset_index.as_ref().unwrap().to_path());
index.push(config.assetIndex.as_ref().unwrap().to_path());
let _ = util::download_file(&config.asset_index.as_ref().unwrap().url.clone(), index.to_str().unwrap(), sx.clone(), "Downloading assets indexes", false).await;
let _ = util::download_file(&config.assetIndex.as_ref().unwrap().url.clone(), index.to_str().unwrap(), sx.clone(), "Downloading assets indexes", false).await;
cnt += 1;
let asset_index = config.asset_index.as_ref().unwrap().url.clone();
let asset_index = config.assetIndex.as_ref().unwrap().url.clone();
overall_size += config.asset_index.as_ref().unwrap().size as usize;
overall_size += config.asset_index.as_ref().unwrap().total_size as usize;
overall_size += config.assetIndex.as_ref().unwrap().size as usize;
overall_size += config.assetIndex.as_ref().unwrap().totalSize as usize;
let assets = crate::minecraft::assets::fetch_assets_list(&asset_index).await.unwrap().objects;
@ -778,24 +778,4 @@ pub async fn get_random_bg() -> Result<Option<String>, Box<dyn Error + Send + Sy
return Ok(Some(["data:image/jpeg;base64,", &BASE64_STANDARD.encode(resp)].concat()));
}
Ok(None)
}
#[derive(Serialize, Deserialize)]
struct RemoteRelease {
tag_name: String,
assets: Vec<RemoteAsset>
}
#[derive(Serialize, Deserialize)]
struct RemoteAsset {
browser_download_url: String
}
pub async fn check_updates() -> Result<Option<String>, Box<dyn Error + Send + Sync>> {
let mut r = surf::get("https://gitea.awain.net/api/v1/repos/alterwain/XCraft/releases/latest").await?;
let release: RemoteRelease = r.body_json().await?;
if release.tag_name != env!("CARGO_PKG_VERSION") && !release.assets.is_empty() {
return Ok(Some(release.assets[0].browser_download_url.clone()));
}
Ok(None)
}

View File

@ -38,7 +38,7 @@ struct App {
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 mut webview_builder = WebViewBuilder::new()
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()) {
@ -47,17 +47,9 @@ impl ApplicationHandler for App {
}
let _ = SENDER.lock().unwrap().as_ref().unwrap().send((uri, None, responder));
})
.with_url("xcraft://custom/ui");
if !cfg!(debug_assertions) {
webview_builder = webview_builder.with_initialization_script(r#"
document.addEventListener("contextmenu", event => event.preventDefault());
"#);
}
let webview = webview_builder
.build(&window)
.unwrap();
.with_url("xcraft://custom/ui")
.build(&window)
.unwrap();
self.window = Some(window);
self.webview = Some(webview);
@ -90,12 +82,6 @@ async fn main() {
if let Some((ui_action, params, responder)) = receiver.recv().await {
let ui_action = &ui_action[16..];
match ui_action {
"github" => {
Command::new("cmd").args(["/C", "start", "https://github.com/alterdekim/XCraft"]).spawn().unwrap();
},
"jquery" => responder.respond(Response::new(include_bytes!("js/jquery.js"))),
"skinview3d" => responder.respond(Response::new(include_bytes!("js/skinview3d.js"))),
"tailwind" => responder.respond(Response::new(include_bytes!("js/tailwind.js"))),
"ui" => responder.respond(Response::new(include_bytes!("www/portable.html"))),
"portable" => {
launcher.config.set_portable(true);
@ -349,20 +335,6 @@ async fn main() {
}
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: [vec!["load_screenshots".to_string()], svec].concat() }).unwrap()));
}
"check_updates" => {
//
if let Ok(Some(file_url)) = launcher::check_updates().await {
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["sidebar_off".to_string(), "show_loading".to_string(), "update_downloads".to_string(), "Updating launcher...".to_string(), "100".to_string()] }).unwrap()));
let current_exe = std::env::current_exe().unwrap();
let mut downloaded_file = std::env::current_dir().unwrap();
downloaded_file.push("launcher_new.exe");
util::simple_download(&file_url, downloaded_file.to_str().unwrap()).await.unwrap();
let _ = std::fs::rename(&current_exe, "old_launcher.bak");
let _ = std::fs::rename(downloaded_file, current_exe);
let _ = std::fs::remove_file("old_launcher.bak");
std::process::exit(0);
}
}
_ => {}
}
}

View File

@ -16,11 +16,9 @@ pub mod versions {
pub r#type: String,
pub url: String,
pub time: String,
#[serde(rename = "releaseTime")]
pub release_time: String,
pub releaseTime: String,
pub sha1: String,
#[serde(rename = "complianceLevel")]
pub compliance_level: u8
pub complianceLevel: u8
}
#[derive(Serialize, Deserialize)]
@ -31,12 +29,9 @@ pub mod versions {
#[derive(Serialize, Deserialize, Clone)]
pub struct VersionConfig {
#[serde(rename = "assetIndex")]
pub asset_index: Option<ConfigAssetIndex>,
#[serde(rename = "mainClass")]
pub main_class: String,
#[serde(rename = "minecraftArguments")]
pub minecraft_arguments: String,
pub assetIndex: Option<ConfigAssetIndex>,
pub mainClass: String,
pub minecraftArguments: String,
pub downloads: Option<ConfigDownloads>,
pub id: String,
pub r#type: String,
@ -132,8 +127,7 @@ pub mod versions {
pub struct ConfigAssetIndex {
pub id: String,
pub sha1: String,
#[serde(rename = "totalSize")]
pub total_size: u64,
pub totalSize: u64,
pub size: u64,
pub url: String
}
@ -244,8 +238,7 @@ pub mod multimc {
#[derive(Serialize, Deserialize)]
pub struct Component {
#[serde(rename = "cachedName")]
pub cached_name: Option<String>,
pub cachedName: Option<String>,
pub version: String,
pub uid: String,
}

View File

@ -21,13 +21,6 @@ pub async fn get_image(url: &str) -> Result<String, Box<dyn Error + Send + Sync>
Ok(format!("data:image/png;base64,{}", base64_string))
}
pub async fn simple_download(url: &str, file_path: &str) -> Result<(), Box<dyn Error + Send + Sync>> {
let bytes = surf::get(url).recv_bytes().await?;
let mut f = File::create(file_path).await?;
f.write_all(&bytes).await?;
Ok(())
}
pub async fn download_file(url: &str, file_path: &str, sender: UnboundedSender<(usize, String)>, status: &str, join: bool) -> Result<(), Box<dyn std::error::Error>> {
let url = url.to_string();
let file_path = file_path.to_string();
@ -54,7 +47,7 @@ pub async fn download_file(url: &str, file_path: &str, sender: UnboundedSender<(
}
});
if join {
let _ = g.await;
g.await;
}
Ok(())
}

View File

@ -3,9 +3,10 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="/skinview3d"></script>
<script src="/jquery"></script>
<script src="/tailwind"></script>
<script src=" https://cdn.jsdelivr.net/npm/skinview3d@3.1.0/bundles/skinview3d.bundle.min.js "></script>
<script src=" https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js "></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>
<style>
::-webkit-scrollbar { display: none; }
body { overflow: hidden; }
@ -22,7 +23,7 @@
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"
>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-device-gamepad"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M2 6m0 2a2 2 0 0 1 2 -2h16a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-16a2 2 0 0 1 -2 -2z" /><path d="M6 12h4m-2 -2v4" /><path d="M15 11l0 .01" /><path d="M18 13l0 .01" /></svg>
<i class="fa-solid fa-gamepad"></i>
<span
class="invisible absolute start-full top-1/2 ms-4 -translate-y-1/2 rounded-sm bg-gray-900 px-2 py-1.5 text-xs font-medium text-white group-hover:visible"
@ -39,7 +40,7 @@
onclick="showSection(this, 'add')"
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"
>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-download"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2 -2v-2" /><path d="M7 11l5 5l5 -5" /><path d="M12 4l0 12" /></svg>
<i class="fa-solid fa-download"></i>
<span
class="invisible absolute start-full top-1/2 ms-4 -translate-y-1/2 rounded-sm bg-gray-900 px-2 py-1.5 text-xs font-medium text-white group-hover:visible"
@ -55,7 +56,7 @@
onclick="showSection(this, 'servers'); showServersSection()"
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"
>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-server-2"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M3 4m0 3a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3z" /><path d="M3 12m0 3a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3z" /><path d="M7 8l0 .01" /><path d="M7 16l0 .01" /><path d="M11 8h6" /><path d="M11 16h6" /></svg>
<i class="fa-solid fa-network-wired"></i>
<span
class="invisible absolute start-full top-1/2 ms-4 -translate-y-1/2 rounded-sm bg-gray-900 px-2 py-1.5 text-xs font-medium text-white group-hover:visible"
@ -71,7 +72,7 @@
onclick="showSection(this, 'accounts'); showAccountsSection()"
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"
>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-users"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 7m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0" /><path d="M3 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2" /><path d="M16 3.13a4 4 0 0 1 0 7.75" /><path d="M21 21v-2a4 4 0 0 0 -3 -3.85" /></svg>
<i class="fa-solid fa-user-group"></i>
<span
class="invisible absolute start-full top-1/2 ms-4 -translate-y-1/2 rounded-sm bg-gray-900 px-2 py-1.5 text-xs font-medium text-white group-hover:visible"
@ -87,7 +88,7 @@
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"
>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-photo-scan"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 8h.01" /><path d="M6 13l2.644 -2.644a1.21 1.21 0 0 1 1.712 0l3.644 3.644" /><path d="M13 13l1.644 -1.644a1.21 1.21 0 0 1 1.712 0l1.644 1.644" /><path d="M4 8v-2a2 2 0 0 1 2 -2h2" /><path d="M4 16v2a2 2 0 0 0 2 2h2" /><path d="M16 4h2a2 2 0 0 1 2 2v2" /><path d="M16 20h2a2 2 0 0 0 2 -2v-2" /></svg>
<i class="fa-solid fa-image"></i>
<span
class="invisible absolute start-full top-1/2 ms-4 -translate-y-1/2 rounded-sm bg-gray-900 px-2 py-1.5 text-xs font-medium text-white group-hover:visible"
@ -103,7 +104,7 @@
onclick="showSection(this, 'settings')"
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"
>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-settings"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z" /><path d="M9 12a3 3 0 1 0 6 0a3 3 0 0 0 -6 0" /></svg>
<i class="fa-solid fa-gear"></i>
<span
class="invisible absolute start-full top-1/2 ms-4 -translate-y-1/2 rounded-sm bg-gray-900 px-2 py-1.5 text-xs font-medium text-white group-hover:visible"
@ -123,7 +124,7 @@
<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">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-info-square-rounded"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 9h.01" /><path d="M11 12h1v4h1" /><path d="M12 3c7.2 0 9 1.8 9 9s-1.8 9 -9 9s-9 -1.8 -9 -9s1.8 -9 9 -9z" /></svg>
<i class="fa-solid fa-info"></i>
</span>
<div class="flex-1">
@ -133,7 +134,16 @@
<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" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-x"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M18 6l-12 12" /><path d="M6 6l12 12" /></svg>
<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>
@ -206,15 +216,15 @@
<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 flex justify-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-world-download"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M21 12a9 9 0 1 0 -9 9" /><path d="M3.6 9h16.8" /><path d="M3.6 15h8.4" /><path d="M11.578 3a17 17 0 0 0 0 18" /><path d="M12.5 3c1.719 2.755 2.5 5.876 2.5 9" /><path d="M18 14v7m-3 -3l3 3l3 -3" /></svg> Download Version
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
onclick="importMultiMcInstance()" class="mt-3 w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded transition flex justify-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-file-zip"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M6 20.735a2 2 0 0 1 -1 -1.735v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2h-1" /><path d="M11 17a2 2 0 0 1 2 2v2a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1v-2a2 2 0 0 1 2 -2z" /><path d="M11 5l-1 0" /><path d="M13 7l-1 0" /><path d="M11 9l-1 0" /><path d="M13 11l-1 0" /><path d="M11 13l-1 0" /><path d="M13 15l-1 0" /></svg> Add MultiMC Instance
onclick="importMultiMcInstance()" 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>
@ -224,9 +234,9 @@
<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 flex flex-col items-center"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-jetpack"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M17 2a4 4 0 0 1 4 4v7a1 1 0 0 1 -1 1h-6a1 1 0 0 1 -1 -1v-1h-2v1a1 1 0 0 1 -1 1h-6a1 1 0 0 1 -1 -1v-7a4 4 0 0 1 8 0v1h2v-1a4 4 0 0 1 4 -4m-4 8v-1h-2v1zm-4 5a1 1 0 0 1 1 1c0 2.623 -.787 4.59 -2.4 5.8a1 1 0 0 1 -1.2 0c-1.613 -1.21 -2.4 -3.177 -2.4 -5.8a1 1 0 0 1 2 0c0 1.532 .308 2.684 .906 3.498l.094 .119l.094 -.12c.558 -.759 .864 -1.813 .902 -3.196l.004 -.301a1 1 0 0 1 1 -1m10 0a1 1 0 0 1 1 1c0 2.623 -.787 4.59 -2.4 5.8a1 1 0 0 1 -1.2 0c-1.613 -1.21 -2.4 -3.177 -2.4 -5.8a1 1 0 0 1 2 0c0 1.532 .308 2.684 .906 3.498l.094 .119l.094 -.12c.558 -.759 .864 -1.813 .902 -3.196l.004 -.301a1 1 0 0 1 1 -1" /></svg>Launcher</button>
<button onClick="setSettingsTab('appearance')" class="py-2 px-4 focus:text-green-500 flex flex-col items-center"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-palette"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 2c5.498 0 10 4.002 10 9c0 1.351 -.6 2.64 -1.654 3.576c-1.03 .914 -2.412 1.424 -3.846 1.424h-2.516a1 1 0 0 0 -.5 1.875a1 1 0 0 1 .194 .14a2.3 2.3 0 0 1 -1.597 3.99l-.156 -.009l.068 .004l-.273 -.004c-5.3 -.146 -9.57 -4.416 -9.716 -9.716l-.004 -.28c0 -5.523 4.477 -10 10 -10m-3.5 6.5a2 2 0 0 0 -1.995 1.85l-.005 .15a2 2 0 1 0 2 -2m8 0a2 2 0 0 0 -1.995 1.85l-.005 .15a2 2 0 1 0 2 -2m-4 -3a2 2 0 0 0 -1.995 1.85l-.005 .15a2 2 0 1 0 2 -2" /></svg>Appearance</button>
<button onClick="setSettingsTab('java')" class="py-2 px-4 focus:text-green-500 flex flex-col items-center"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-mug"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M3.903 4.008l.183 -.008h10.828a2.08 2.08 0 0 1 2.086 2.077v.923h1.5c1.917 0 3.5 1.477 3.5 3.333v2.334c0 1.856 -1.583 3.333 -3.5 3.333h-1.663a5.33 5.33 0 0 1 -5.17 4h-4.334c-2.944 0 -5.333 -2.375 -5.333 -5.308v-8.618a2.08 2.08 0 0 1 1.903 -2.066m13.097 9.992h1.5c.843 0 1.5 -.613 1.5 -1.333v-2.334c0 -.72 -.657 -1.333 -1.5 -1.333h-1.5z" /></svg>Java</button>
<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">
@ -293,8 +303,8 @@
</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 flex justify-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-device-floppy"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M6 4h10l4 4v10a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2" /><path d="M12 14m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M14 4l0 4l-6 0l0 -4" /></svg> Save
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>
@ -384,13 +394,7 @@
</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"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-plus"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 5l0 14" /><path d="M5 12l14 0" /></svg></div>
</div>
<div class="absolute bottom-0 right-0 p-2">
<a class="text-3xl text-gray-300 cursor-pointer" onClick="openGithub()">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-brand-github"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M5.315 2.1c.791 -.113 1.9 .145 3.333 .966l.272 .161l.16 .1l.397 -.083a13.3 13.3 0 0 1 4.59 -.08l.456 .08l.396 .083l.161 -.1c1.385 -.84 2.487 -1.17 3.322 -1.148l.164 .008l.147 .017l.076 .014l.05 .011l.144 .047a1 1 0 0 1 .53 .514a5.2 5.2 0 0 1 .397 2.91l-.047 .267l-.046 .196l.123 .163c.574 .795 .93 1.728 1.03 2.707l.023 .295l.007 .272c0 3.855 -1.659 5.883 -4.644 6.68l-.245 .061l-.132 .029l.014 .161l.008 .157l.004 .365l-.002 .213l-.003 3.834a1 1 0 0 1 -.883 .993l-.117 .007h-6a1 1 0 0 1 -.993 -.883l-.007 -.117v-.734c-1.818 .26 -3.03 -.424 -4.11 -1.878l-.535 -.766c-.28 -.396 -.455 -.579 -.589 -.644l-.048 -.019a1 1 0 0 1 .564 -1.918c.642 .188 1.074 .568 1.57 1.239l.538 .769c.76 1.079 1.36 1.459 2.609 1.191l.001 -.678l-.018 -.168a5.03 5.03 0 0 1 -.021 -.824l.017 -.185l.019 -.12l-.108 -.024c-2.976 -.71 -4.703 -2.573 -4.875 -6.139l-.01 -.31l-.004 -.292a5.6 5.6 0 0 1 .908 -3.051l.152 -.222l.122 -.163l-.045 -.196a5.2 5.2 0 0 1 .145 -2.642l.1 -.282l.106 -.253a1 1 0 0 1 .529 -.514l.144 -.047l.154 -.03z" /></svg>
</a>
<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>
@ -466,10 +470,6 @@
}
}
function openGithub() {
$.get("github", processParams);
}
function showAccountsSection() {
$.post({url: "fetch_credentials_list" }, processParams);
}
@ -774,8 +774,6 @@
$.get("fetch_bg", processParams);
showAccountsSection();
$.get("check_updates", processParams);
$('#slim-skin').on('change', function () {
$.post({ url: "set_skin_model", data: JSON.stringify({ params: [accountNick, accountDomain, $(this).is(':checked')+""] }) }, processParams);
});