Revert "Merge pull request #2 from MojaveMF/main"

This reverts commit 8471de75e8, reversing
changes made to ea35090634.
This commit is contained in:
PrintedScript 2023-11-05 00:32:38 +08:00
parent b3503988d9
commit 300481fc9a
6 changed files with 293 additions and 536 deletions

92
Cargo.lock generated
View File

@ -812,16 +812,6 @@ dependencies = [
"tempfile",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]]
name = "num-traits"
version = "0.2.16"
@ -912,12 +902,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "parking_lot"
version = "0.12.1"
@ -1214,15 +1198,6 @@ dependencies = [
"digest",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
@ -1286,7 +1261,7 @@ dependencies = [
[[package]]
name = "syntax_bootstrapper"
version = "1.2.1"
version = "1.2.0"
dependencies = [
"chrono",
"colored",
@ -1298,8 +1273,6 @@ dependencies = [
"reqwest",
"term_size",
"tokio",
"tracing",
"tracing-subscriber",
"winreg 0.51.0",
"winres",
"zip-extract",
@ -1348,16 +1321,6 @@ dependencies = [
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "time"
version = "0.1.45"
@ -1472,59 +1435,22 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
[[package]]
name = "tracing"
version = "0.1.40"
version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
"cfg-if",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.32"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2"
dependencies = [
"log",
"once_cell",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77"
dependencies = [
"nu-ansi-term",
"sharded-slab",
"smallvec",
"thread_local",
"tracing-core",
"tracing-log",
]
[[package]]
@ -1577,12 +1503,6 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "vcpkg"
version = "0.2.15"

View File

@ -17,8 +17,6 @@ tokio = { version = "1.32.0", features=["full"]}
futures-util = "0.3.28"
md5 = "0.7.0"
zip-extract = "0.1.2"
tracing = "0.1.40"
tracing-subscriber = "0.3.17"
[target.'cfg(windows)'.dependencies]
winreg = "0.51.0"

View File

@ -1,8 +0,0 @@
.d8888b. Y88b d88P 888b 888 88888888888 d8888 Y88b d88P
d88P Y88b Y88b d88P 8888b 888 888 d88888 Y88b d88P
Y88b. Y88o88P 88888b 888 888 d88P888 Y88o88P
\"Y888b. Y888P 888Y88b 888 888 d88P 888 Y888P
\"Y88b. 888 888 Y88b888 888 d88P 888 d888b
\"888 888 888 Y88888 888 d88P 888 d88888b
Y88b d88P 888 888 Y8888 888 d8888888888 d88P Y88b
\"Y8888P\" 888 888 Y888 888 d88P 888 d88P Y88b

View File

@ -1,19 +0,0 @@
pub const FILES_TO_DOWNLOAD: [[&str; 2]; 17] = [
["SyntaxApp.zip", "./"],
["NPSyntaxProxy.zip", "./"],
["SyntaxProxy.zip", "./"],
["Libraries.zip", "./"],
["redist.zip", "./"],
["content-textures.zip", "./content/textures"],
["content-textures2.zip", "./content/textures"],
["content-textures3.zip", "./content/textures"],
["content-terrain.zip", "./content/terrain"],
["content-fonts.zip", "./content/fonts"],
["content-sounds.zip", "./content/sounds"],
["content-scripts.zip", "./content/scripts"],
["content-sky.zip", "./content/sky"],
["content-music.zip", "./content/music"],
["content-particles.zip", "./content/particles"],
["2018client.zip", "./Client2018"],
["2020client.zip", "./Client2020"],
];

View File

@ -1,83 +1,159 @@
use colored::*;
use std::path::PathBuf;
use reqwest::Client;
use dirs::data_local_dir;
use futures_util::StreamExt;
use md5;
use metadata::LevelFilter;
use reqwest::Client;
use std::path::PathBuf;
use tokio::task::JoinSet;
use zip_extract;
mod constants;
mod util;
use constants::*;
use tracing::*;
use util::*;
#[cfg(not(target_os = "windows"))]
use std::io::prelude::*;
#[cfg(not(target_os = "windows"))]
use std::os::unix::fs::FileExt;
#[cfg(target_os = "windows")]
use std::os::windows::prelude::FileExt;
#[cfg(target_os = "windows")]
use winreg::enums::*;
#[cfg(target_os = "windows")]
use winreg::RegKey;
#[cfg(not(target_os = "windows"))]
use std::io::prelude::*;
#[cfg(not(target_os = "windows"))]
use std::os::unix::fs::FileExt;
fn info( message : &str ) {
let time = chrono::Local::now().format("%H:%M:%S").to_string();
println!("[{}] [{}] {}", time.bold().blue(), "INFO".bold().green(), message);
}
fn error( message : &str ) {
let time = chrono::Local::now().format("%H:%M:%S").to_string();
println!("[{}] [{}] {}", time.bold().blue(), "ERROR".bold().red(), message);
}
#[cfg(debug_assertions)]
const DEBUG: bool = true;
#[cfg(debug_assertions)]
const MAX_TRACING_LEVEL: LevelFilter = LevelFilter::DEBUG;
#[cfg(not(debug_assertions))]
const DEBUG: bool = false;
#[cfg(not(debug_assertions))]
const MAX_TRACING_LEVEL: LevelFilter = LevelFilter::ERROR;
fn debug( message : &str ) {
let time = chrono::Local::now().format("%H:%M:%S").to_string();
println!("[{}] [{}] {}", time.bold().blue(), "DEBUG".bold().yellow(), message);
}
const ASCII_ART: &str = include_str!("./ascii.txt");
#[cfg(not(debug_assertions))]
fn debug( message : &str ) {}
pub async fn http_get( client: &Client ,url: &str ) -> Result<String, reqwest::Error> {
debug(&format!("{} {}", "GET".green(), url.bright_blue()));
let response = client.get(url).send().await;
if (response.is_err()) {
debug(&format!("Failed to fetch {}", url.bright_blue()));
return Err(response.err().unwrap());
}
let response_body = response.unwrap().text().await.unwrap();
Ok(response_body)
}
pub async fn download_file( client: &Client, url: &str, path: &PathBuf ) {
debug(&format!("{} {}", "GET".green(), url.bright_blue()));
let response = client.get(url).send().await.unwrap();
let content_length = response.content_length().unwrap();
debug(&format!("Content Length: {}", content_length));
let time = chrono::Local::now().format("%H:%M:%S").to_string();
let pg_bar_str = " {spinner:.green} [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})";
let progress_bar = indicatif::ProgressBar::new(content_length);
let progress_style = indicatif::ProgressStyle::default_bar()
.template(
format!("{}\n{}",
format!(
"[{}] [{}] {}",
time.bold().blue(),
"INFO".bold().green(),
&format!("Downloading {}", &url.bright_blue())
),
pg_bar_str).as_str()
)
.unwrap().progress_chars("#>-");
progress_bar.set_style(progress_style);
progress_bar.set_message("Downloading File");
let file = std::fs::File::create(path).unwrap();
let mut downloaded: u64 = 0;
let mut stream = response.bytes_stream();
while let Some(item) = stream.next().await {
let chunk = item.or(Err(format!("Error while downloading file"))).unwrap();
#[cfg(target_os = "windows")]
{
file.seek_write(chunk.as_ref(), downloaded).unwrap();
}
#[cfg(not(target_os = "windows"))]
{
file.write_at(chunk.as_ref(), downloaded).unwrap();
}
let new = std::cmp::min(downloaded + (chunk.len() as u64), content_length);
downloaded = new;
progress_bar.set_position(new);
}
progress_bar.finish();
info(format!("Finished downloading {}", url.green()).as_str());
}
pub async fn download_file_prefix( client: &Client, url: &str, path_prefix : &PathBuf ) -> PathBuf {
let path = path_prefix.join(generate_md5(url).await);
download_file(client, url, &path).await;
return path;
}
pub async fn generate_md5( input : &str ) -> String {
let hashed_input = md5::compute(input.as_bytes());
return format!("{:x}", hashed_input);
}
pub async fn create_folder_if_not_exists( path: &PathBuf ) {
if !path.exists() {
info(&format!("Creating folder {}", path.to_str().unwrap().bright_blue()));
std::fs::create_dir_all(path).unwrap();
}
}
fn get_installation_directory() -> PathBuf {
return PathBuf::from(data_local_dir().unwrap().to_str().unwrap()).join("Syntax");
}
#[tokio::main]
async fn main() {
// Clear the terminal before printing the startup text
#[cfg(target_os = "windows")]
{
std::process::Command::new("cmd")
.args(&["/c", "cls"])
.spawn()
.expect("cls command failed to start")
.wait()
.expect("failed to wait");
.args(&["/c", "cls"])
.spawn()
.expect("cls command failed to start")
.wait()
.expect("failed to wait");
}
#[cfg(not(target_os = "windows"))]
{
std::process::Command::new("clear").spawn().unwrap();
}
tracing_subscriber::fmt()
.with_max_level(MAX_TRACING_LEVEL)
.pretty()
.init();
let args: Vec<String> = std::env::args().collect();
let base_url: &str = "www.syntax.eco";
let mut setup_url: &str = "setup.syntax.eco";
let fallback_setup_url: &str = "d2f3pa9j0u8v6f.cloudfront.net";
let mut bootstrapper_filename: &str = "SyntaxPlayerLauncher.exe";
let base_url : &str = "www.syntax.eco";
let mut setup_url : &str = "setup.syntax.eco";
let fallback_setup_url : &str = "d2f3pa9j0u8v6f.cloudfront.net";
let mut bootstrapper_filename :&str = "SyntaxPlayerLauncher.exe";
#[cfg(not(target_os = "windows"))]
{
bootstrapper_filename = "SyntaxPlayerLinuxLauncher";
}
let build_date = include_str!(concat!(env!("OUT_DIR"), "/build_date.txt"));
let startup_text = ASCII_ART.to_owned();
let bootstrapper_info = format!(
"{} | Build Date: {} | Version: {}",
base_url,
build_date,
env!("CARGO_PKG_VERSION"),
);
let startup_text = format!("
.d8888b. Y88b d88P 888b 888 88888888888 d8888 Y88b d88P
d88P Y88b Y88b d88P 8888b 888 888 d88888 Y88b d88P
Y88b. Y88o88P 88888b 888 888 d88P888 Y88o88P
\"Y888b. Y888P 888Y88b 888 888 d88P 888 Y888P
\"Y88b. 888 888 Y88b888 888 d88P 888 d888b
\"888 888 888 Y88888 888 d88P 888 d88888b
Y88b d88P 888 888 Y8888 888 d8888888888 d88P Y88b
\"Y8888P\" 888 888 Y888 888 d88P 888 d88P Y88b
{} | Build Date: {} | Version: {}", base_url ,build_date, env!("CARGO_PKG_VERSION"));
// Format the startup text to be centered
let mut terminal_width = 80;
@ -85,80 +161,51 @@ async fn main() {
terminal_width = w;
}
if terminal_width < 80 {
print!(
"{}\n",
format!(
"SYNTAX Bootstrapper | {} | Build Date: {} | Version: {}",
base_url,
build_date,
env!("CARGO_PKG_VERSION")
)
.to_string()
.magenta()
.cyan()
.italic()
.on_black()
); // Fallback message
print!("{}\n", format!("SYNTAX Bootstrapper | {} | Build Date: {} | Version: {}", base_url, build_date, env!("CARGO_PKG_VERSION")).to_string().magenta().cyan().italic().on_black()); // Fallback message
} else {
let startup_text_lines = startup_text.lines().collect::<Vec<&str>>();
//println!("{}", startup_text.bold().blue().on_black());
// print all lines except the last one
for line in startup_text_lines {
for line in &startup_text_lines[0..startup_text_lines.len() - 1] {
let spaces = (terminal_width - line.len()) / 2;
let formatted_line = format!("{}{}", " ".repeat(spaces), line);
println!("{}", formatted_line.bright_magenta().italic().on_black());
}
// print last line as a different color
println!(
"{}\n",
bootstrapper_info.magenta().cyan().italic().on_black()
);
let last_line = startup_text_lines[startup_text_lines.len() - 1];
let spaces = (terminal_width - last_line.len()) / 2;
let last_line = format!("{}{}", " ".repeat(spaces), last_line);
println!("{}\n", last_line.magenta().cyan().italic().on_black());
}
let http_client: Client = reqwest::Client::builder().no_gzip().build().unwrap();
debug!(
"Setup Server: {} | Base Server: {}",
setup_url.bright_blue(),
base_url.bright_blue()
);
debug!("Fetching latest client version from setup server");
let latest_client_version: String;
let latest_client_version_response =
http_get(&http_client, &format!("https://{}/version", setup_url)).await;
let http_client: Client = reqwest::Client::builder()
.no_gzip()
.build()
.unwrap();
debug(format!("Setup Server: {} | Base Server: {}", setup_url.bright_blue(), base_url.bright_blue()).as_str());
debug("Fetching latest client version from setup server");
let latest_client_version : String;
let latest_client_version_response = http_get(&http_client ,&format!("https://{}/version", setup_url)).await;
match latest_client_version_response {
Ok(latest_client_version_result) => {
debug!(
"Latest Client Version: {}",
latest_client_version_result.bright_blue()
);
debug(&format!("Latest Client Version: {}", latest_client_version_result.bright_blue()));
latest_client_version = latest_client_version_result;
}
},
Err(e) => {
error!("Failed to fetch latest client version from setup server: [{}], attempting to fallback to {}", e.to_string().bright_red(), fallback_setup_url.bright_blue());
let fallback_client_version_response = http_get(
&http_client,
&format!("https://{}/version", fallback_setup_url),
)
.await;
error(&format!("Failed to fetch latest client version from setup server: [{}], attempting to fallback to {}", e.to_string().bright_red(), fallback_setup_url.bright_blue()));
let fallback_client_version_response = http_get(&http_client ,&format!("https://{}/version", fallback_setup_url)).await;
match fallback_client_version_response {
Ok(fallback_client_version_result) => {
info!(
"Successfully fetched latest client version from fallback setup server: {}",
fallback_setup_url.bright_blue()
);
debug!(
"Latest Client Version: {}",
fallback_client_version_result.bright_blue()
);
info(&format!("Successfully fetched latest client version from fallback setup server: {}", fallback_setup_url.bright_blue()));
debug(&format!("Latest Client Version: {}", fallback_client_version_result.bright_blue()));
latest_client_version = fallback_client_version_result;
setup_url = fallback_setup_url;
}
},
Err(e) => {
error!("Failed to fetch latest client version from fallback setup server: {}, are you connected to the internet?", e);
error(&format!("Failed to fetch latest client version from fallback setup server: {}, are you connected to the internet?", e));
std::thread::sleep(std::time::Duration::from_secs(10));
std::process::exit(0);
}
@ -167,58 +214,35 @@ async fn main() {
}
// Wait for the latest client version to be fetched
info!(
"Latest Client Version: {}",
latest_client_version.cyan().underline()
);
debug!("Setup Server: {}", setup_url.cyan().underline());
info(&format!("Latest Client Version: {}", latest_client_version.cyan().underline()));
debug(&format!("Setup Server: {}", setup_url.cyan().underline()));
let installation_directory = get_installation_directory();
debug!(
"Installation Directory: {}",
installation_directory.to_str().unwrap().bright_blue()
);
debug(&format!("Installation Directory: {}", installation_directory.to_str().unwrap().bright_blue()));
create_folder_if_not_exists(&installation_directory).await;
let versions_directory = installation_directory.join("Versions");
debug!(
"Versions Directory: {}",
versions_directory.to_str().unwrap().bright_blue()
);
debug(&format!("Versions Directory: {}", versions_directory.to_str().unwrap().bright_blue()));
create_folder_if_not_exists(&versions_directory).await;
let temp_downloads_directory = installation_directory.join("Downloads");
debug!(
"Temp Downloads Directory: {}",
temp_downloads_directory.to_str().unwrap().bright_blue()
);
debug(&format!("Temp Downloads Directory: {}", temp_downloads_directory.to_str().unwrap().bright_blue()));
create_folder_if_not_exists(&temp_downloads_directory).await;
let current_version_directory = versions_directory.join(format!("{}", latest_client_version));
debug!(
"Current Version Directory: {}",
current_version_directory.to_str().unwrap().bright_blue()
);
debug(&format!("Current Version Directory: {}", current_version_directory.to_str().unwrap().bright_blue()));
create_folder_if_not_exists(&current_version_directory).await;
let latest_bootstrapper_path = current_version_directory.join(bootstrapper_filename);
// Is the program currently running from the latest version directory?
let current_exe_path = std::env::current_exe().unwrap();
// If the current exe path is not in the current version directory, then we need to run the latest bootstrapper ( download if needed )
if !current_exe_path.starts_with(&current_version_directory) && !DEBUG {
if !current_exe_path.starts_with(&current_version_directory) {
// Check if the latest bootstrapper is downloaded
if !latest_bootstrapper_path.exists() {
info!("Downloading the latest bootstrapper and restarting");
info("Downloading the latest bootstrapper and restarting");
// Download the latest bootstrapper
download_to_file(
&http_client,
&format!(
"https://{}/{}-{}",
setup_url, latest_client_version, bootstrapper_filename
),
&latest_bootstrapper_path,
)
.await;
download_file(&http_client, &format!("https://{}/{}-{}", setup_url, latest_client_version, bootstrapper_filename), &latest_bootstrapper_path).await;
}
// Run the latest bootstrapper ( with the same arguments passed to us ) and exit
#[cfg(target_os = "windows")]
@ -226,20 +250,12 @@ async fn main() {
let mut command = std::process::Command::new(latest_bootstrapper_path.clone());
command.args(&args[1..]);
match command.spawn() {
Ok(_) => {}
Ok(_) => {},
Err(e) => {
debug!(&format!("Bootstrapper errored with error {}", e));
debug(&format!("Bootstrapper errored with error {}", e));
info("Found bootstrapper was corrupted! Downloading...");
std::fs::remove_file(latest_bootstrapper_path.clone()).unwrap();
download_file(
&http_client,
&format!(
"https://{}/{}-{}",
setup_url, latest_client_version, bootstrapper_filename
),
&latest_bootstrapper_path,
)
.await;
download_file(&http_client, &format!("https://{}/{}-{}", setup_url, latest_client_version, bootstrapper_filename), &latest_bootstrapper_path).await;
command.spawn().expect("Bootstrapper is still corrupted.");
std::thread::sleep(std::time::Duration::from_secs(20));
}
@ -248,13 +264,9 @@ async fn main() {
#[cfg(not(target_os = "windows"))]
{
// Make sure the latest bootstrapper is executable
std::process::Command::new("chmod")
.arg("+x")
.arg(latest_bootstrapper_path.to_str().unwrap())
.spawn()
.unwrap();
std::process::Command::new("chmod").arg("+x").arg(latest_bootstrapper_path.to_str().unwrap()).spawn().unwrap();
info!("We need permission to run the latest bootstrapper");
info("We need permission to run the latest bootstrapper");
let mut command = std::process::Command::new(latest_bootstrapper_path);
command.args(&args[1..]);
command.spawn().unwrap();
@ -263,13 +275,13 @@ async fn main() {
}
// Looks like we are running from the latest version directory, so we can continue with the update process
// Check for "AppSettings.xml" in the current version directory
// Check for "AppSettings.xml" in the current version directory
// If it doesent exist, then we got either a fresh directory or a corrupted installation
// So delete the every file in the current version directory except for the Bootstrapper itself
let app_settings_path = current_version_directory.join("AppSettings.xml");
let client_executable_path = current_version_directory.join("SyntaxPlayerBeta.exe");
if !app_settings_path.exists() || !client_executable_path.exists() {
info!("Downloading the latest client files, this may take a while.");
info("Downloading the latest client files, this may take a while.");
for entry in std::fs::read_dir(&current_version_directory).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
@ -282,70 +294,117 @@ async fn main() {
}
}
let version_url_prefix = format!("https://{}/{}-", setup_url, latest_client_version);
let VersionURLPrefix = format!("https://{}/{}-", setup_url, latest_client_version);
let SyntaxAppZip : PathBuf = download_file_prefix(&http_client, format!("{}SyntaxApp.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
let NPSyntaxProxyZip : PathBuf = download_file_prefix(&http_client, format!("{}NPSyntaxProxy.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
let SyntaxProxyZip : PathBuf = download_file_prefix(&http_client, format!("{}SyntaxProxy.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
let LibrariesZip : PathBuf = download_file_prefix(&http_client, format!("{}Libraries.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
let RedistZip : PathBuf = download_file_prefix(&http_client, format!("{}redist.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
/* Use a joisnet to run multiple async functions at once */
let ContentTexturesZip : PathBuf = download_file_prefix(&http_client, format!("{}content-textures.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
let ContentTextures2Zip : PathBuf = download_file_prefix(&http_client, format!("{}content-textures2.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
let ContentTextures3Zip : PathBuf = download_file_prefix(&http_client, format!("{}content-textures3.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
let ContentTerrainZip : PathBuf = download_file_prefix(&http_client, format!("{}content-terrain.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
let ContentFontsZip : PathBuf = download_file_prefix(&http_client, format!("{}content-fonts.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
let ContentSoundsZip : PathBuf = download_file_prefix(&http_client, format!("{}content-sounds.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
let ContentScriptsZip : PathBuf = download_file_prefix(&http_client, format!("{}content-scripts.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
let ContentSkyZip : PathBuf = download_file_prefix(&http_client, format!("{}content-sky.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
let ContentMusicZip : PathBuf = download_file_prefix(&http_client, format!("{}content-music.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
let ContentParticles : PathBuf = download_file_prefix(&http_client, format!("{}content-particles.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
let mut set = JoinSet::new();
let ShadersZip : PathBuf = download_file_prefix(&http_client, format!("{}shaders.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
for [value, _] in FILES_TO_DOWNLOAD {
set.spawn(download_and_extract(
value.to_string(),
version_url_prefix.clone(),
current_version_directory.clone(),
));
let Client2018Zip : PathBuf = download_file_prefix(&http_client, format!("{}2018client.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
let Client2020Zip : PathBuf = download_file_prefix(&http_client, format!("{}2020client.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
info("Download finished, extracting files.");
fn extract_to_dir( zip_file : &PathBuf, target_dir : &PathBuf ) {
let zip_file_cursor = std::fs::File::open(zip_file).unwrap();
zip_extract::extract(zip_file_cursor, target_dir, false).unwrap();
}
extract_to_dir(&SyntaxAppZip, &current_version_directory);
extract_to_dir(&NPSyntaxProxyZip, &current_version_directory);
extract_to_dir(&SyntaxProxyZip, &current_version_directory);
extract_to_dir(&LibrariesZip, &current_version_directory);
extract_to_dir(&RedistZip, &current_version_directory);
while let Some(value) = set.join_next().await {
value.unwrap()
}
let content_directory = current_version_directory.join("content");
let platform_content_directory = current_version_directory.join("PlatformContent");
let shaders_directory = current_version_directory.join("shaders");
info!("Binary installed");
create_folder_if_not_exists(&content_directory).await;
create_folder_if_not_exists(&platform_content_directory).await;
create_folder_if_not_exists(&shaders_directory).await;
/* Convert to async due to this being a slow function */
let fonts_directory = content_directory.join("fonts");
let music_directory = content_directory.join("music");
let particles_directory = content_directory.join("particles");
let sky_directory = content_directory.join("sky");
let sounds_directory = content_directory.join("sounds");
let textures_directory = content_directory.join("textures");
let scripts_directory = content_directory.join("scripts");
// Redacted for lagging vscode
create_folder_if_not_exists(&fonts_directory).await;
create_folder_if_not_exists(&music_directory).await;
create_folder_if_not_exists(&particles_directory).await;
create_folder_if_not_exists(&sky_directory).await;
create_folder_if_not_exists(&sounds_directory).await;
create_folder_if_not_exists(&textures_directory).await;
extract_to_dir(&ContentTexturesZip, &textures_directory);
extract_to_dir(&ContentTextures2Zip, &textures_directory);
extract_to_dir(&ContentFontsZip, &fonts_directory);
extract_to_dir(&ContentSoundsZip, &sounds_directory);
extract_to_dir(&ContentSkyZip, &sky_directory);
extract_to_dir(&ContentMusicZip, &music_directory);
extract_to_dir(&ContentParticles, &particles_directory);
extract_to_dir(&ContentScriptsZip, &scripts_directory);
let platform_pc_directory = platform_content_directory.join("pc");
create_folder_if_not_exists(&platform_pc_directory).await;
let terrain_directory = platform_pc_directory.join("terrain");
let textures_directory = platform_pc_directory.join("textures");
create_folder_if_not_exists(&terrain_directory).await;
create_folder_if_not_exists(&textures_directory).await;
extract_to_dir(&ContentTerrainZip, &terrain_directory);
extract_to_dir(&ContentTextures3Zip, &textures_directory);
extract_to_dir(&ShadersZip, &shaders_directory);
let client_2018_directory = current_version_directory.join("Client2018");
create_folder_if_not_exists(&client_2018_directory).await;
extract_to_dir(&Client2018Zip, &client_2018_directory);
let client_2020_directory = current_version_directory.join("Client2020");
create_folder_if_not_exists(&client_2020_directory).await;
extract_to_dir(&Client2020Zip, &client_2020_directory);
info("Finished extracting files, cleaning up.");
std::fs::remove_dir_all(&temp_downloads_directory).unwrap();
// Install the syntax-player scheme in the registry
info!("Installing syntax-player scheme");
info("Installing syntax-player scheme");
#[cfg(target_os = "windows")]
{
let hkey_current_user = RegKey::predef(HKEY_CURRENT_USER);
let hkey_classes_root: RegKey =
hkey_current_user.open_subkey("Software\\Classes").unwrap();
let hkey_classes_root : RegKey = hkey_current_user.open_subkey("Software\\Classes").unwrap();
let hkey_syntax_player = hkey_classes_root.create_subkey("syntax-player").unwrap().0;
let hkey_syntax_player_shell = hkey_syntax_player.create_subkey("shell").unwrap().0;
let hkey_syntax_player_shell_open =
hkey_syntax_player_shell.create_subkey("open").unwrap().0;
let hkey_syntax_player_shell_open_command = hkey_syntax_player_shell_open
.create_subkey("command")
.unwrap()
.0;
let hkey_syntax_player_shell_open = hkey_syntax_player_shell.create_subkey("open").unwrap().0;
let hkey_syntax_player_shell_open_command = hkey_syntax_player_shell_open.create_subkey("command").unwrap().0;
let defaulticon = hkey_syntax_player.create_subkey("DefaultIcon").unwrap().0;
hkey_syntax_player_shell_open_command
.set_value(
"",
&format!("\"{}\" \"%1\"", current_exe_path.to_str().unwrap()),
)
.unwrap();
defaulticon
.set_value("", &format!("\"{}\",0", current_exe_path.to_str().unwrap()))
.unwrap();
hkey_syntax_player
.set_value("", &format!("URL: Syntax Protocol"))
.unwrap();
hkey_syntax_player_shell_open_command.set_value("", &format!("\"{}\" \"%1\"", current_exe_path.to_str().unwrap())).unwrap();
defaulticon.set_value("", &format!("\"{}\",0", current_exe_path.to_str().unwrap())).unwrap();
hkey_syntax_player.set_value("", &format!("URL: Syntax Protocol")).unwrap();
hkey_syntax_player.set_value("URL Protocol", &"").unwrap();
}
#[cfg(not(target_os = "windows"))]
{
// Linux support
// We have to write a .desktop file to ~/.local/share/applications
let desktop_file_path = dirs::data_local_dir()
.unwrap()
.join("applications")
.join("syntax-player.desktop");
let desktop_file_path = dirs::data_local_dir().unwrap().join("applications").join("syntax-player.desktop");
let desktop_file = format!(
"[Desktop Entry]
"[Desktop Entry]
Name=Syntax Launcher
Exec={} %u
Terminal=true
@ -355,37 +414,31 @@ Icon={}
StartupWMClass=SyntaxLauncher
Categories=Game;
Comment=Syntax Launcher
",
current_exe_path.to_str().unwrap(),
current_exe_path.to_str().unwrap()
);
", current_exe_path.to_str().unwrap(), current_exe_path.to_str().unwrap());
std::fs::write(desktop_file_path, desktop_file).unwrap();
// We also have to write a mimeapps.list file to ~/.config
let mimeapps_list_path = dirs::config_dir().unwrap().join("mimeapps.list");
let mimeapps_list = format!(
"[Default Applications]
"[Default Applications]
x-scheme-handler/syntax-player=syntax-player.desktop
"
);
");
std::fs::write(mimeapps_list_path, mimeapps_list).unwrap();
// We also have to write a mimeapps.list file to ~/.local/share
let mimeapps_list_path = dirs::data_local_dir().unwrap().join("mimeapps.list");
let mimeapps_list = format!(
"[Default Applications]
"[Default Applications]
x-scheme-handler/syntax-player=syntax-player.desktop
"
);
");
std::fs::write(mimeapps_list_path, mimeapps_list).unwrap();
}
// Write the AppSettings.xml file
let app_settings_xml = format!(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<Settings>
<ContentFolder>content</ContentFolder>
<BaseUrl>https://{}</BaseUrl>
</Settings>",
base_url
</Settings>", base_url
);
std::fs::write(app_settings_path, app_settings_xml).unwrap();
@ -403,25 +456,17 @@ x-scheme-handler/syntax-player=syntax-player.desktop
// Parse the arguments passed to the bootstrapper
// Looks something like "syntax-player://1+launchmode:play+gameinfo:TICKET+placelauncherurl:https://www.syntax.eco/Game/placelauncher.ashx?placeId=660&t=TICKET+k:l"
debug!("Arguments Passed: {}", args.join(" ").bright_blue());
debug(&format!("Arguments Passed: {}", args.join(" ").bright_blue()));
if args.len() == 1 {
// Just open the website
#[cfg(target_os = "windows")]
{
std::process::Command::new("cmd")
.arg("/c")
.arg("start")
.arg("https://www.syntax.eco/games")
.spawn()
.unwrap();
std::process::Command::new("cmd").arg("/c").arg("start").arg("https://www.syntax.eco/games").spawn().unwrap();
std::process::exit(0);
}
#[cfg(not(target_os = "windows"))]
{
std::process::Command::new("xdg-open")
.arg("https://www.syntax.eco/games")
.spawn()
.unwrap();
std::process::Command::new("xdg-open").arg("https://www.syntax.eco/games").spawn().unwrap();
std::process::exit(0);
}
}
@ -434,29 +479,30 @@ x-scheme-handler/syntax-player=syntax-player.desktop
let mut authentication_ticket = String::new();
let mut join_script = String::new();
let mut client_year = String::new();
for arg in main_args {
let mut arg_split = arg.split(":");
let key = arg_split.next().unwrap();
let value = if arg_split.clone().count() > 0 {
arg_split.collect::<Vec<&str>>().join(":")
} else {
String::new()
};
debug!("{}: {}", key.bright_blue(), value.bright_blue());
let value =
if arg_split.clone().count() > 0 {
arg_split.collect::<Vec<&str>>().join(":")
} else {
String::new()
};
debug(&format!("{}: {}", key.bright_blue(), value.bright_blue()));
match key {
"launchmode" => {
launch_mode = value.to_string();
}
},
"gameinfo" => {
authentication_ticket = value.to_string();
}
},
"placelauncherurl" => {
join_script = value.to_string();
}
},
"clientyear" => {
client_year = value.to_string();
}
},
_ => {}
}
}
@ -468,22 +514,18 @@ x-scheme-handler/syntax-player=syntax-player.desktop
let wine_path_file = installation_directory.join("winepath.txt");
if wine_path_file.exists() {
let custom_wine = std::fs::read_to_string(wine_path_file).unwrap();
info!("Using custom wine binary: {}", custom_wine.bright_blue());
info(&format!("Using custom wine binary: {}", custom_wine.bright_blue()));
} else {
info!("No custom wine binary specified, using default wine command");
info!("If you want to use a custom wine binary, please create a file at {} with the path to the wine binary", wine_path_file.to_str().unwrap());
info("No custom wine binary specified, using default wine command");
info(format!("If you want to use a custom wine binary, please create a file at {} with the path to the wine binary", wine_path_file.to_str().unwrap()).as_str());
}
}
let client_executable_path: PathBuf;
debug!("{}", &client_year.to_string());
let client_executable_path : PathBuf;
debug(&client_year.to_string());
if client_year == "2018" {
client_executable_path = current_version_directory
.join("Client2018")
.join("SyntaxPlayerBeta.exe");
client_executable_path = current_version_directory.join("Client2018").join("SyntaxPlayerBeta.exe");
} else if client_year == "2020" {
client_executable_path = current_version_directory
.join("Client2020")
.join("SyntaxPlayerBeta.exe");
client_executable_path = current_version_directory.join("Client2020").join("SyntaxPlayerBeta.exe");
} else {
client_executable_path = current_version_directory.join("SyntaxPlayerBeta.exe");
}
@ -492,25 +534,17 @@ x-scheme-handler/syntax-player=syntax-player.desktop
let app_settings_path = current_version_directory.join("AppSettings.xml");
std::fs::remove_file(app_settings_path).unwrap();
error!("Failed to run SyntaxPlayerBeta.exe, is your antivirus removing it? The bootstrapper will attempt to redownload the client on next launch.");
error("Failed to run SyntaxPlayerBeta.exe, is your antivirus removing it? The bootstrapper will attempt to redownload the client on next launch.");
std::thread::sleep(std::time::Duration::from_secs(20));
std::process::exit(0);
}
match launch_mode.as_str() {
"play" => {
info!("Launching SYNTAX");
info("Launching SYNTAX");
#[cfg(target_os = "windows")]
{
{
let mut command = std::process::Command::new(client_executable_path);
command.args(&[
"--play",
"--authenticationUrl",
format!("https://{}/Login/Negotiate.ashx", base_url).as_str(),
"--authenticationTicket",
authentication_ticket.as_str(),
"--joinScriptUrl",
format!("{}", join_script.as_str()).as_str(),
]);
command.args(&["--play","--authenticationUrl", format!("https://{}/Login/Negotiate.ashx", base_url).as_str(), "--authenticationTicket", authentication_ticket.as_str(), "--joinScriptUrl", format!("{}",join_script.as_str()).as_str()]);
command.spawn().unwrap();
std::thread::sleep(std::time::Duration::from_secs(5));
std::process::exit(0);
@ -519,27 +553,18 @@ x-scheme-handler/syntax-player=syntax-player.desktop
{
// We have to launch the game through wine
let mut command = std::process::Command::new(custom_wine);
command.args(&[
client_executable_path.to_str().unwrap(),
"--play",
"--authenticationUrl",
format!("https://{}/Login/Negotiate.ashx", base_url).as_str(),
"--authenticationTicket",
authentication_ticket.as_str(),
"--joinScriptUrl",
format!("{}", join_script.as_str()).as_str(),
]);
command.args(&[client_executable_path.to_str().unwrap(), "--play","--authenticationUrl", format!("https://{}/Login/Negotiate.ashx", base_url).as_str(), "--authenticationTicket", authentication_ticket.as_str(), "--joinScriptUrl", format!("{}",join_script.as_str()).as_str()]);
// We must wait for the game to exit before exiting the bootstrapper
let mut child = command.spawn().unwrap();
child.wait().unwrap();
std::thread::sleep(std::time::Duration::from_secs(1));
std::process::exit(0);
}
}
},
_ => {
error!("Unknown launch mode, exiting.");
error("Unknown launch mode, exiting.");
std::thread::sleep(std::time::Duration::from_secs(10));
std::process::exit(0);
}
}
}
}

View File

@ -1,159 +0,0 @@
/*
Move all generic functions into this area
*/
use colored::*;
use dirs::data_local_dir;
use futures_util::StreamExt;
use md5;
use reqwest::Client;
use reqwest::ClientBuilder;
use std::io::Cursor;
use std::path::PathBuf;
use tokio::fs;
use tokio::fs::create_dir_all;
use zip_extract;
use crate::constants::*;
use tracing::*;
#[cfg(not(target_os = "windows"))]
use std::io::prelude::*;
/*
#[cfg(not(target_os = "windows"))]
use std::os::unix::fs::FileExt;
#[cfg(target_os = "windows")]
use std::os::windows::prelude::FileExt;
*/
#[cfg(target_os = "windows")]
use winreg::enums::*;
#[cfg(target_os = "windows")]
use winreg::RegKey;
pub async fn http_get(client: &Client, url: &str) -> Result<String, reqwest::Error> {
debug!("{} {}", "GET".green(), url.bright_blue());
let response = client.get(url).send().await;
if response.is_err() {
debug!("Failed to fetch {}", url.bright_blue());
return Err(response.err().unwrap());
}
let response_body = response.unwrap().text().await.unwrap();
Ok(response_body)
}
pub async fn download_file(client: &Client, url: &str) -> Vec<u8> {
debug!("{} {}", "GET".green(), url.bright_blue());
let response = client.get(url).send().await.unwrap();
/* Why through over a visual bug? */
let content_length = response.content_length().or(Some(0)).unwrap();
debug!("Content Length: {}", content_length);
let time = chrono::Local::now().format("%H:%M:%S").to_string();
let pg_bar_str =
" {spinner:.green} [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})";
let progress_bar = indicatif::ProgressBar::new(content_length);
let progress_style = indicatif::ProgressStyle::default_bar()
.template(
format!(
"{}\n{}",
format!(
"[{}] [{}] {}",
time.bold().blue(),
"INFO".bold().green(),
&format!("Downloading {}", &url.bright_blue())
),
pg_bar_str
)
.as_str(),
)
.unwrap()
.progress_chars("#>-");
progress_bar.set_style(progress_style);
progress_bar.set_message("Downloading File");
let mut buffer: Vec<u8> = vec![];
let mut downloaded: u64 = 0;
let mut stream = response.bytes_stream();
while let Some(item) = stream.next().await {
let chunk = item
.or(Err(format!("Error while downloading file")))
.unwrap();
buffer.write_all(chunk.as_ref()).unwrap();
let new = std::cmp::min(downloaded + (chunk.len() as u64), content_length);
downloaded = new;
progress_bar.set_position(new);
}
progress_bar.finish();
info!("Finished downloading {}", url.green());
return buffer;
}
pub async fn download_to_file(client: &Client, url: &str, path: &PathBuf) {
let bytes = download_file(client, url).await;
fs::write(path, bytes).await.unwrap();
}
pub async fn download_file_prefix<T: Into<String>>(client: &Client, url: T) -> Vec<u8> {
let string: String = url.into();
let buffer = download_file(client, &string).await;
return buffer;
}
/*
pub async fn generate_md5(input: &str) -> String {
let hashed_input = md5::compute(input.as_bytes());
return format!("{:x}", hashed_input);
}*/
pub async fn create_folder_if_not_exists(path: &PathBuf) {
if !path.exists() {
info!("Creating folder {}", path.to_str().unwrap().bright_blue());
fs::create_dir_all(path).await.unwrap();
}
}
pub fn get_installation_directory() -> PathBuf {
return PathBuf::from(data_local_dir().unwrap().to_str().unwrap()).join("Syntax");
}
/* Why was this in the main function i will never know */
pub async fn extract_to_dir(zip_file: &Vec<u8>, target_dir: &PathBuf) {
let zip_file_cursor = Cursor::new(zip_file);
zip_extract::extract(zip_file_cursor, target_dir, false).unwrap();
}
fn get_location_from_file_name<T: AsRef<str>>(file_name: T) -> String {
let file_name = file_name.as_ref();
for [first, last] in FILES_TO_DOWNLOAD {
if first == file_name {
return last.to_owned();
}
}
let formated = format!("Is not a valid file {}", file_name);
error!("{}", formated);
panic!("{}", formated)
}
pub async fn download_and_extract<T: Into<String>, T2: Into<String>, P: Into<PathBuf>>(
file_name: T,
url_prefix: T2,
extract_location: P,
) {
let http_client = ClientBuilder::default().build().unwrap();
let file_name: String = file_name.into();
let url_prefix: String = url_prefix.into();
let extract_location: PathBuf = extract_location.into();
let buffer = download_file_prefix(&http_client, format!("{}{file_name}", url_prefix)).await;
drop(http_client);
drop(url_prefix);
let dir = extract_location.join(get_location_from_file_name(&file_name));
create_dir_all(&dir).await.unwrap();
info!("Extracting file {}", file_name);
extract_to_dir(&buffer, &dir).await;
drop(buffer);
info!("File {} installed to {:?}", file_name, dir.display());
}