This commit is contained in:
PrintedScript 2023-09-05 13:49:58 +08:00
commit c43efb5d96
10 changed files with 2459 additions and 0 deletions

2
.cargo/config.toml Normal file
View File

@ -0,0 +1,2 @@
[target.'cfg(all(windows, target_env = "msvc"))']
rustflags = ["-C", "target-feature=+crt-static"]

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

1879
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

32
Cargo.toml Normal file
View File

@ -0,0 +1,32 @@
[package]
name = "syntax_bootstrapper"
version = "1.0.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
term_size = "0.3.2"
colored = "2.0.4"
chrono = "0.4.26"
dirs = "5.0.1"
indicatif = "0.17.6"
reqwest = { version = "0.11.20", features = ["stream"]}
futures = "0.3.28"
tokio = { version = "1.32.0", features=["full"]}
futures-util = "0.3.28"
md5 = "0.7.0"
zip-extract = "0.1.2"
[target.'cfg(windows)'.dependencies]
winreg = "0.51.0"
[build-dependencies]
chrono = "0.4.26"
winres = "0.1.12"
[profile.release]
strip = true
lto = true
opt-level = "z"
codegen-units = 1

BIN
assets/Bootstrapper.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

30
build.rs Normal file
View File

@ -0,0 +1,30 @@
use std::fs;
use std::env;
use winres::WindowsResource;
fn main() {
// Get the current build date and time
let build_date = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC").to_string();
// Write the build date to a file
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = format!("{}/build_date.txt", out_dir);
fs::write(&dest_path, &build_date).unwrap();
if env::var_os("CARGO_CFG_WINDOWS").is_some() {
// Add a version resource to the executable
let mut res = WindowsResource::new();
res.set_icon("assets/Bootstrapper.ico");
res.set_language(0x0409); // US English
res.set("FileVersion", env!("CARGO_PKG_VERSION"));
res.set("FileDescription", "SYNTAX Windows Bootstrapper");
res.set("ProductName", "SYNTAX Bootstrapper");
res.set("ProductVersion", env!("CARGO_PKG_VERSION"));
res.set("InternalName", "SYNTAX Bootstrapper");
res.set("OriginalFilename", "SyntaxPlayerLauncher.exe");
res.set("CompanyName", "SYNTAX Corporation");
res.set("LegalCopyright", "Copyright (c) 2023");
res.compile().unwrap();
}
println!("cargo:rerun-if-changed=build.rs");
}

15
readme.md Normal file
View File

@ -0,0 +1,15 @@
# Syntax Boostrapper
A bootstrapper written in Rust meant to replace the old Roblox Launcher
You are welcome to add support to other platforms ( *MacOS* ) or add improvements to the bootstrapper
## Building
For Windows:
> win-build-release.bat
For Linux:
> cargo build --release
If you want to build the debug version of the bootstrapper for development you can run
> cargo build

489
src/main.rs Normal file
View File

@ -0,0 +1,489 @@
use colored::*;
use std::path::PathBuf;
use reqwest::Client;
use dirs::data_local_dir;
use futures_util::StreamExt;
use md5;
use zip_extract;
#[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)]
fn debug( message : &str ) {
let time = chrono::Local::now().format("%H:%M:%S").to_string();
println!("[{}] [{}] {}", time.bold().blue(), "DEBUG".bold().yellow(), message);
}
#[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_body = client.get(url).send().await?.text().await?;
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("cls").spawn().unwrap();
}
#[cfg(not(target_os = "windows"))]
{
std::process::Command::new("clear").spawn().unwrap();
}
let args: Vec<String> = std::env::args().collect();
let base_url : &str = "www.syntax.eco";
let setup_url : &str = "setup.syntax.eco";
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 = 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;
if let Some((w, _h)) = term_size::dimensions() {
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
} 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[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
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(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 = http_get(&http_client ,&format!("https://{}/version", setup_url)).await.unwrap();
// Wait for the latest client version to be fetched
info(&format!("Latest Client Version: {}", latest_client_version.cyan().underline()));
let installation_directory = get_installation_directory();
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(&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(&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(&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) {
// Check if the latest bootstrapper is downloaded
if !latest_bootstrapper_path.exists() {
info("Downloading the latest bootstrapper and restarting");
// Download the latest bootstrapper
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")]
{
let mut command = std::process::Command::new(latest_bootstrapper_path);
command.args(&args[1..]);
command.spawn().unwrap();
}
#[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();
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();
}
std::process::exit(0);
}
// 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
// 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");
if !app_settings_path.exists() {
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();
if path.is_file() {
if path != current_exe_path {
std::fs::remove_file(path).unwrap();
}
} else {
std::fs::remove_dir_all(path).unwrap();
}
}
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;
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 ShadersZip : PathBuf = download_file_prefix(&http_client, format!("{}shaders.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);
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");
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;
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");
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);
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");
#[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_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 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.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 = format!(
"[Desktop Entry]
Name=Syntax Launcher
Exec={} %u
Terminal=true
Type=Application
MimeType=x-scheme-handler/syntax-player;
Icon={}
StartupWMClass=SyntaxLauncher
Categories=Game;
Comment=Syntax Launcher
", 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]
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]
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\"?>
<Settings>
<ContentFolder>content</ContentFolder>
<BaseUrl>http://{}</BaseUrl>
</Settings>", base_url
);
std::fs::write(app_settings_path, app_settings_xml).unwrap();
// Check for any other version directories and deletes them
for entry in std::fs::read_dir(&versions_directory).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
if path.is_dir() {
if path != current_version_directory {
std::fs::remove_dir_all(path).unwrap();
}
}
}
}
// 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(&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::exit(0);
}
#[cfg(not(target_os = "windows"))]
{
std::process::Command::new("xdg-open").arg("https://www.syntax.eco/games").spawn().unwrap();
std::process::exit(0);
}
}
let main_args = &args[1];
let main_args = main_args.replace("syntax-player://", "");
let main_args = main_args.split("+").collect::<Vec<&str>>();
let mut launch_mode = String::new();
let mut authentication_ticket = String::new();
let mut join_script = 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(&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();
},
_ => {}
}
}
let custom_wine = "wine";
#[cfg(not(target_os = "windows"))]
{
// We allow user to specify the wine binary path in installation_directory/winepath.txt
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(&format!("Using custom wine binary: {}", custom_wine.bright_blue()));
} else {
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());
}
}
match launch_mode.as_str() {
"play" => {
info("Launching SYNTAX");
#[cfg(target_os = "windows")]
{
let mut command = std::process::Command::new(current_version_directory.join("SyntaxPlayerBeta.exe"));
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);
}
#[cfg(not(target_os = "windows"))]
{
// We have to launch the game through wine
let mut command = std::process::Command::new(custom_wine);
command.args(&[current_version_directory.join("SyntaxPlayerBeta.exe").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.");
std::thread::sleep(std::time::Duration::from_secs(10));
std::process::exit(0);
}
}
}

BIN
tools/upx.exe Normal file

Binary file not shown.

11
win-build-release.bat Normal file
View File

@ -0,0 +1,11 @@
@echo off
rustup target add i686-pc-windows-msvc
:: 32-bit build
cargo build --release --target=i686-pc-windows-msvc
:: Compression disabled as it gave some false positives on virustotal
:: May re-enable in the future if we ever get a signing certificate
:: tools\upx.exe target\release\*.exe
:: Renames the executable to SyntaxPlayerLauncher.exe from syntax_bootstrapper.exe
ren target\i686-pc-windows-msvc\release\syntax_bootstrapper.exe SyntaxPlayerLauncher.exe