modified: src/launcher.rs
modified: src/main.rs modified: src/minecraft.rs modified: src/util.rs modified: src/www/portable.html deleted: src/www/profile_customization.html
This commit is contained in:
parent
e90b025568
commit
eebef351ad
@ -152,6 +152,36 @@ impl Launcher {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn login_user_server(&mut self, server: String, username: String, password: String) -> (bool, &str) {
|
||||
let mut session_server_port: u16 = 8999;
|
||||
let mut server_port: u16 = 25565;
|
||||
let mut domain = server.clone();
|
||||
if let Some(index) = server.find("#") {
|
||||
let (a,b) = server.split_at(index+1);
|
||||
session_server_port = b.parse().unwrap();
|
||||
domain = a[..a.len()-1].to_string();
|
||||
}
|
||||
|
||||
if let Some(index) = domain.find(":") {
|
||||
let dmc = domain.clone();
|
||||
let (a,b) = dmc.split_at(index+1);
|
||||
domain = a[..a.len()-1].to_string();
|
||||
server_port = b.parse().unwrap();
|
||||
}
|
||||
|
||||
println!("Server information: {}:{} session={}", domain, server_port, session_server_port);
|
||||
|
||||
match minecraft::session::try_login(domain.clone(), session_server_port, username.clone(), password.clone(), self.config.allow_http).await {
|
||||
Ok(status) => match status {
|
||||
SignUpResponse::ServerError => (false, "Internal server error"),
|
||||
SignUpResponse::BadCredentials => (false, "Username or password is not valid"),
|
||||
SignUpResponse::UserAlreadyExists => (false, "User already exists"),
|
||||
SignUpResponse::Registered(uuid) => self.save_server_info(uuid, username, password, domain, session_server_port, server_port)
|
||||
}
|
||||
Err(_e) => (false, "Internal server error")
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_servers_list(&self) -> Vec<(String, String, Option<String>)> {
|
||||
let mut v = Vec::new();
|
||||
let servers = self.config.servers();
|
||||
|
38
src/main.rs
38
src/main.rs
@ -159,16 +159,41 @@ async fn main() {
|
||||
|
||||
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: v }).unwrap()));
|
||||
}
|
||||
"get_skin" => {
|
||||
let params = params.unwrap().params;
|
||||
let nickname = params[0].clone();
|
||||
let domain = params[1].clone();
|
||||
if let Some(server) = launcher.find_credentials(&nickname, &domain) {
|
||||
let resp = util::get_image(&["http", if launcher.config.allow_http { "" } else { "s" }, "://", &domain, ":", &server.session_server_port.to_string(), "/api/skin/s", &server.credentials.uuid].concat()).await;
|
||||
if let Ok(resp) = resp {
|
||||
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["get_skin".to_string(), resp] }).unwrap()));
|
||||
}
|
||||
}
|
||||
}
|
||||
"get_cape" => {
|
||||
let params = params.unwrap().params;
|
||||
let nickname = params[0].clone();
|
||||
let domain = params[1].clone();
|
||||
if let Some(server) = launcher.find_credentials(&nickname, &domain) {
|
||||
let resp = util::get_image(&["http", if launcher.config.allow_http { "" } else { "s" }, "://", &domain, ":", &server.session_server_port.to_string(), "/api/cape/a", &server.credentials.uuid].concat()).await;
|
||||
if let Ok(resp) = resp {
|
||||
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["get_cape".to_string(), resp] }).unwrap()));
|
||||
}
|
||||
}
|
||||
}
|
||||
"upload_skin" => {
|
||||
let params = params.unwrap().params;
|
||||
if let Some(server) = launcher.find_credentials(¶ms[0], ¶ms[1]) {
|
||||
if let Some(skin_path) = FileDialog::new().add_filter("Images", &["png"]).pick_file() {
|
||||
|
||||
let msg = launcher.upload_skin(skin_path, &server.credentials.uuid, &server.credentials.password, &[if launcher.config.allow_http {"http"} else {"https"}, "://", &server.domain, ":", &server.session_server_port.to_string(), "/api/upload"].concat()).await;
|
||||
if let Ok(msg) = msg {
|
||||
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["add_server_response".to_string(), String::new(), msg] }).unwrap()));
|
||||
} else {
|
||||
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["add_server_response".to_string(), String::new(), "Error uploading new skin".to_string()] }).unwrap()));
|
||||
match msg {
|
||||
Ok(msg ) => {
|
||||
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["add_server_response".to_string(), String::new(), msg] }).unwrap()));
|
||||
},
|
||||
Err(_e) => {
|
||||
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["add_server_response".to_string(), String::new(), "Error uploading new skin".to_string()] }).unwrap()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -258,6 +283,11 @@ async fn main() {
|
||||
// todo: implement error notifications
|
||||
}
|
||||
}
|
||||
"add_server_login" => {
|
||||
let params = ¶ms.unwrap().params;
|
||||
let (status, msg) = launcher.login_user_server(params[0].clone(), params[1].clone(), params[2].clone()).await;
|
||||
responder.respond(Response::new(serde_json::to_vec(&UIMessage { params: vec!["add_server_response".to_string(), status.to_string(), msg.to_string()] }).unwrap()));
|
||||
}
|
||||
"add_server" => {
|
||||
let params = ¶ms.unwrap().params;
|
||||
let (status, msg) = launcher.register_user_server(params[0].clone(), params[1].clone(), params[2].clone()).await;
|
||||
|
@ -206,6 +206,26 @@ pub mod session {
|
||||
_ => Ok(SignUpResponse::ServerError)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn try_login(server_domain: String, port: u16, username: String, password: String, allow_http: bool) -> Result<SignUpResponse, Box<dyn Error + Send + Sync>> {
|
||||
let request = SignUpRequest { username: username.clone(), password };
|
||||
let mut r = surf::post([if allow_http { "http://".to_string() } else { "https://".to_string() }, server_domain, ":".to_string(), port.to_string(), "/api/login".to_string()].concat())
|
||||
.body_json(&request)
|
||||
.unwrap()
|
||||
.await?;
|
||||
|
||||
let b= r.body_bytes().await.unwrap();
|
||||
|
||||
match r.status() {
|
||||
surf::StatusCode::BadRequest => Ok(SignUpResponse::BadCredentials),
|
||||
surf::StatusCode::Conflict => Ok(SignUpResponse::UserAlreadyExists),
|
||||
surf::StatusCode::Ok => {
|
||||
let response: ResponseUUID = serde_json::from_slice(&b).unwrap();
|
||||
Ok(SignUpResponse::Registered(response.uuid))
|
||||
},
|
||||
_ => Ok(SignUpResponse::ServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod multimc {
|
||||
|
10
src/util.rs
10
src/util.rs
@ -1,3 +1,7 @@
|
||||
use std::error::Error;
|
||||
|
||||
use base64::prelude::BASE64_STANDARD;
|
||||
use base64::Engine;
|
||||
use futures::AsyncReadExt;
|
||||
use rand::{distr::Alphanumeric, Rng};
|
||||
use tokio::{fs::File, io::AsyncWriteExt};
|
||||
@ -11,6 +15,12 @@ pub fn random_string(len: usize) -> String {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub async fn get_image(url: &str) -> Result<String, Box<dyn Error + Send + Sync>> {
|
||||
let bytes = surf::get(url).recv_bytes().await?;
|
||||
let base64_string = BASE64_STANDARD.encode(&bytes);
|
||||
Ok(format!("data:image/png;base64,{}", base64_string))
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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>
|
||||
@ -14,13 +15,13 @@
|
||||
<body class="flex bg-gray-100 h-screen">
|
||||
<div id="sidebar" class="flex h-screen bg-white w-16 flex-col justify-between border-e border-gray-100 hidden">
|
||||
<div>
|
||||
<div class="inline-flex size-16 items-center justify-center">
|
||||
<!--<div class="inline-flex size-16 items-center justify-center">
|
||||
<a href="#" onclick="showAppearance(this)">
|
||||
<span class="grid size-10 place-content-center rounded-lg bg-green-500 text-xs text-white">
|
||||
Me
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>-->
|
||||
|
||||
<div class="border-t border-gray-100">
|
||||
<div class="px-2">
|
||||
@ -179,7 +180,7 @@
|
||||
<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">
|
||||
<div id="installation-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">Welcome to XCraft</h1>
|
||||
<p class="mt-2 text-gray-600">Choose how you want to set up Minecraft.</p>
|
||||
|
||||
@ -317,45 +318,51 @@
|
||||
<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">
|
||||
<span class="text-gray-600 font-medium">Username:</span>
|
||||
<span id="account-name" class="text-gray-600 font-medium"></span>
|
||||
</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">
|
||||
<button onclick="uploadSkin()"
|
||||
class="bg-green-500 hover:bg-green-700 text-white px-4 py-2 rounded">
|
||||
Upload
|
||||
</button>
|
||||
</div>
|
||||
<div id="appearance-section" class="xsection flex">
|
||||
<div>
|
||||
<canvas id="skin_container"></canvas>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 text-left">
|
||||
<label class="inline-flex items-center cursor-pointer">
|
||||
<input id="slim-skin" 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">Slim skin</span>
|
||||
</label>
|
||||
</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">
|
||||
<button onclick="uploadCape()"
|
||||
class="bg-green-500 hover:bg-green-700 text-white px-4 py-2 rounded">
|
||||
Upload
|
||||
</button>
|
||||
</div>
|
||||
<div class="bg-white shadow-lg rounded-xl p-6 w-96 text-center">
|
||||
<h2 class="text-2xl font-semibold text-gray-700">Account Settings</h2>
|
||||
|
||||
<div class="mt-6 text-left">
|
||||
<span class="text-gray-600 font-medium">Username:</span>
|
||||
<span id="account-name" class="text-gray-600 font-medium"></span>
|
||||
</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">
|
||||
<button onclick="uploadSkin()"
|
||||
class="bg-green-500 hover:bg-green-700 text-white px-4 py-2 rounded">
|
||||
Upload
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 text-left">
|
||||
<label class="inline-flex items-center cursor-pointer">
|
||||
<input id="slim-skin" 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">Slim skin</span>
|
||||
</label>
|
||||
</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">
|
||||
<button onclick="uploadCape()"
|
||||
class="bg-green-500 hover:bg-green-700 text-white px-4 py-2 rounded">
|
||||
Upload
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="accounts-section" class="xsection grid grid-cols-3 gap-4 p-6 w-fill hidden">
|
||||
|
||||
</div>
|
||||
@ -377,12 +384,19 @@
|
||||
|
||||
<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>
|
||||
|
||||
|
||||
<div class="flex w-full">
|
||||
<button
|
||||
onclick="addServerInstanceLogin()"
|
||||
class="mt-4 w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded transition">
|
||||
Login
|
||||
</button>
|
||||
<button
|
||||
onclick="addServerInstance()"
|
||||
class="ms-2 mt-4 w-full bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition">
|
||||
Sign up
|
||||
</button>
|
||||
</div>
|
||||
<!-- Footer -->
|
||||
<p class="mt-4 text-sm text-gray-500">alterdekim</p>
|
||||
</div>
|
||||
@ -404,49 +418,62 @@
|
||||
for( let i = 0; i < params.length; i++ ) {
|
||||
if( params[i].startsWith("show_") ) {
|
||||
showSection(undefined, params[i].substring(5));
|
||||
} else if( params[i] == "sidebar_on" ) {
|
||||
$("#sidebar").removeClass('hidden');
|
||||
} else if( params[i] == "sidebar_off") {
|
||||
$("#sidebar").addClass('hidden');
|
||||
}
|
||||
|
||||
if( params[i] == "show_add" ) {
|
||||
showAddSection();
|
||||
} else if( params[i] == "set_downloadable_versions" ) {
|
||||
setDownloadableVersions(params.slice(i+1));
|
||||
break;
|
||||
} else if( params[i] == "update_downloads" ) {
|
||||
updateDownloads(params.slice(i+1));
|
||||
switch(params[i]) {
|
||||
case "sidebar_on":
|
||||
$("#sidebar").removeClass('hidden');
|
||||
break;
|
||||
} else if( params[i] == "show_instances") {
|
||||
showInstancesSection();
|
||||
} else if( params[i] == "set_instances_list" ) {
|
||||
setInstancesList(params.slice(i+1));
|
||||
break;
|
||||
} else if( params[i] == "fetch_servers_list" ) {
|
||||
setServersList(params.slice(i+1));
|
||||
break;
|
||||
} else if( params[i] == "locate_java" ) {
|
||||
setJavaPath(params[i+1]);
|
||||
break;
|
||||
} else if( params[i] == "add_server_response" ) {
|
||||
addServerResponse(params[i+1], params[i+2]);
|
||||
break;
|
||||
} 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++;
|
||||
} else if( params[i] == "update_logs" ) {
|
||||
updateLogs(params[i+1]);
|
||||
break;
|
||||
} else if( params[i] == "fetch_credentials_list" ) {
|
||||
setCredentialsList(params.slice(i+1));
|
||||
break;
|
||||
case "sidebar_off":
|
||||
$("#sidebar").addClass('hidden');
|
||||
break;
|
||||
case "show_add":
|
||||
showAddSection();
|
||||
break;
|
||||
case "set_downloadable_versions":
|
||||
setDownloadableVersions(params.slice(i+1));
|
||||
return;
|
||||
case "update_downloads":
|
||||
updateDownloads(params.slice(i+1));
|
||||
return;
|
||||
case "show_instances":
|
||||
showInstancesSection();
|
||||
break;
|
||||
case "set_instances_list":
|
||||
setInstancesList(params.slice(i+1));
|
||||
return;
|
||||
case "fetch_servers_list":
|
||||
setServersList(params.slice(i+1));
|
||||
return;
|
||||
case "locate_java":
|
||||
setJavaPath(params[i+1]);
|
||||
return;
|
||||
case "add_server_response":
|
||||
addServerResponse(params[i+1], params[i+2]);
|
||||
return;
|
||||
case "fetch_settings_response":
|
||||
setSettings(params.slice(i+1));
|
||||
return;
|
||||
case "load_screenshots":
|
||||
setScreenshots(params.slice(i+1));
|
||||
return;
|
||||
case "fetch_bg":
|
||||
setBackground(params[i+1]);
|
||||
return;
|
||||
case "update_logs":
|
||||
updateLogs(params[i+1]);
|
||||
return;
|
||||
case "fetch_credentials_list":
|
||||
setCredentialsList(params.slice(i+1));
|
||||
return;
|
||||
case "get_skin":
|
||||
skinViewer.loadSkin(params[i+1]);
|
||||
return;
|
||||
case "get_cape":
|
||||
skinViewer.loadCape(params[i+1]);
|
||||
return;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -547,6 +574,15 @@
|
||||
|
||||
function addServerResponse(status, msg) {
|
||||
showPopup(msg);
|
||||
updateSkinPreview();
|
||||
}
|
||||
|
||||
function addServerInstanceLogin() {
|
||||
let server = $("#server_address").val();
|
||||
let username = $("#server_username").val();
|
||||
let password = $("#server_password").val();
|
||||
|
||||
$.post({url: "add_server_login", data: JSON.stringify({ params: [server, username, password] }) }, processParams);
|
||||
}
|
||||
|
||||
function addServerInstance() {
|
||||
@ -574,9 +610,9 @@
|
||||
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 class="h-fill ms-2 w-32">
|
||||
<h2 class="text-sm font-semibold truncate">` + params[i+1] + `</h2>
|
||||
<h2 class="text-xs font-semibold truncate">` + params[i] + `</h2>
|
||||
</div>
|
||||
</div>`;
|
||||
$("#instances-section").append(instance);
|
||||
@ -591,9 +627,15 @@
|
||||
accountDomain = domain;
|
||||
}
|
||||
|
||||
function updateSkinPreview() {
|
||||
$.post({url: "get_skin", data: JSON.stringify({ params: [accountNick, accountDomain] })}, processParams);
|
||||
$.post({url: "get_cape", data: JSON.stringify({ params: [accountNick, accountDomain] })}, processParams);
|
||||
}
|
||||
|
||||
function showAppearance(obj) {
|
||||
if( accountNick != undefined && accountDomain != undefined ) {
|
||||
$("#account-name").html(accountNick);
|
||||
updateSkinPreview();
|
||||
showSection(obj, 'appearance');
|
||||
}
|
||||
}
|
||||
@ -616,10 +658,10 @@
|
||||
if( i == 0 && params.length >= 2 ) {
|
||||
setAccount(params[i+1], params[i]);
|
||||
}
|
||||
let instance = `<div onclick="setAccount('`+params[i+1]+`', '`+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">
|
||||
<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>
|
||||
let instance = `<div onclick="setAccount('`+params[i+1]+`', '`+params[i]+`'); showAppearance()" 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="h-fill ms-2 w-32">
|
||||
<h2 class="text-sm font-semibold truncate">`+params[i+1]+`</h2>
|
||||
<h2 class="text-xs font-semibold truncate">`+params[i]+`</h2>
|
||||
</div>
|
||||
</div>`;
|
||||
$("#accounts-section").append(instance);
|
||||
@ -631,9 +673,9 @@
|
||||
for( let i = 0; i < params.length; i+=3) {
|
||||
let instance = `<div onclick="runServerConnection('`+params[i]+`', '`+params[i+1]+`')" class="server-instance 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]+`</h2>
|
||||
<h2 class="text-sm font-semibold">`+params[i+1]+`</h2>
|
||||
<div class="h-fill ms-2 w-32">
|
||||
<h2 class="text-sm font-semibold truncate">`+params[i]+`</h2>
|
||||
<h2 class="text-xs font-semibold truncate">`+params[i+1]+`</h2>
|
||||
</div>
|
||||
</div>`;
|
||||
$("#servers-section").append(instance);
|
||||
@ -732,6 +774,8 @@
|
||||
$("#"+section+"-section").removeClass('hidden');
|
||||
}
|
||||
|
||||
var skinViewer = undefined;
|
||||
|
||||
$( document ).ready(async function() {
|
||||
$.get("check_installation", processParams);
|
||||
$.get("fetch_settings", processParams);
|
||||
@ -742,6 +786,15 @@
|
||||
$.post({ url: "set_skin_model", data: JSON.stringify({ params: [accountNick, accountDomain, $(this).is(':checked')+""] }) }, processParams);
|
||||
});
|
||||
|
||||
skinViewer = new skinview3d.SkinViewer({
|
||||
canvas: document.getElementById("skin_container"),
|
||||
width: 300,
|
||||
height: 400,
|
||||
skin: "img/skin.png"
|
||||
});
|
||||
|
||||
skinViewer.autoRotate = true;
|
||||
|
||||
setInterval(function() {
|
||||
if( !$("#loading-section").hasClass("hidden") ) {
|
||||
$.get("check_download_status", processParams);
|
||||
|
@ -1,60 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Profile Customization</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
</head>
|
||||
<body class="bg-gray-100 flex justify-center items-center h-screen">
|
||||
|
||||
<div class="bg-white shadow-lg rounded-xl p-6 w-96 text-center">
|
||||
<h2 class="text-2xl font-semibold text-gray-700">Customize Profile</h2>
|
||||
|
||||
<!-- Avatar Preview -->
|
||||
<div class="flex flex-col items-center mt-4">
|
||||
<img id="avatar-preview" src="https://www.minecraft.net/etc.clientlibs/minecraft/clientlibs/main/resources/img/minecraft-creeper-face.jpg"
|
||||
alt="User Avatar" class="w-16 h-16 rounded-full border-4 border-gray-300">
|
||||
</div>
|
||||
|
||||
<!-- Upload Avatar -->
|
||||
<label class="mt-4 cursor-pointer text-green-500 underline block">
|
||||
Change Avatar
|
||||
<input type="file" id="avatar-upload" class="hidden" accept="image/*">
|
||||
</label>
|
||||
|
||||
<!-- Username Input -->
|
||||
<input type="text" id="username" placeholder="Enter username"
|
||||
class="mt-4 w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500">
|
||||
|
||||
<!-- Skin Upload -->
|
||||
<label class="mt-4 cursor-pointer text-green-500 underline block">
|
||||
Upload Skin (.png)
|
||||
<input type="file" id="skin-upload" class="hidden" accept="image/png">
|
||||
</label>
|
||||
|
||||
<!-- Save Button -->
|
||||
<button onclick="saveProfile()"
|
||||
class="mt-4 w-full bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition">
|
||||
Save Changes
|
||||
</button>
|
||||
|
||||
<p class="mt-4 text-sm text-gray-500">Minecraft Launcher v1.0</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('avatar-upload').addEventListener('change', function(event) {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
document.getElementById('avatar-preview').src = URL.createObjectURL(file);
|
||||
}
|
||||
});
|
||||
|
||||
function saveProfile() {
|
||||
const username = document.getElementById('username').value;
|
||||
alert(Profile Updated: ${username});
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user