This commit is contained in:
lx
2026-07-07 13:29:39 -04:00
commit 070e12d0e3
11 changed files with 2563 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/target
Generated
+1880
View File
File diff suppressed because it is too large Load Diff
+33
View File
@@ -0,0 +1,33 @@
[package]
name = "syntax_bootstrapper"
version = "1.4.1"
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"
sha1 = "0.10.6"
[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
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 PrintedScript
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

+31
View File
@@ -0,0 +1,31 @@
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/revsim.ico");
res.set_language(0x0409); // US English
res.set("FileVersion", env!("CARGO_PKG_VERSION"));
res.set("FileDescription", "Revsim Windows Bootstrapper");
res.set("ProductName", "Revsim Bootstrapper");
res.set("ProductVersion", env!("CARGO_PKG_VERSION"));
res.set("InternalName", "Revsim Bootstrapper");
res.set("OriginalFilename", "RevsimPlayerLauncher.exe");
res.set("CompanyName", "Revsim Corporation");
res.set("LegalCopyright", "Copyright (c) 1998 REVSIM Corporation");
res.compile().unwrap();
}
println!("cargo:rerun-if-changed=build.rs");
}
+15
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
+571
View File
@@ -0,0 +1,571 @@
use chrono::format;
use colored::*;
use std::path::PathBuf;
use reqwest::Client;
use dirs::data_local_dir;
use futures_util::StreamExt;
use md5;
use zip_extract;
use sha1::{Sha1, Digest};
#[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 = 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();
}
}
pub async fn get_sha1_hash_of_file( path: &PathBuf ) -> String {
let mut file = std::fs::File::open(path).unwrap();
let mut hasher = Sha1::new();
std::io::copy(&mut file, &mut hasher).unwrap();
let hash = hasher.finalize();
return format!("{:x}", hash);
}
fn get_installation_directory() -> PathBuf {
return PathBuf::from(data_local_dir().unwrap().to_str().unwrap()).join("Revsim");
}
#[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");
}
#[cfg(not(target_os = "windows"))]
{
std::process::Command::new("clear").spawn().unwrap();
}
let args: Vec<String> = std::env::args().collect();
let base_url : &str = "revsim.xyz";
let mut setup_url : &str = "clients.orcbin.com";
let fallback_setup_url : &str = "d2f3pa9j0u8v6f.cloudfront.net";
let mut bootstrapper_filename :&str = "RevsimPlayerLauncher.exe";
#[cfg(not(target_os = "windows"))]
{
bootstrapper_filename = "RevsimPlayerLinuxLauncher";
}
let build_date = include_str!(concat!(env!("OUT_DIR"), "/build_date.txt"));
let startup_text = format!(r#"
_______ ________ __ __ ______ ______ __ __
| \ | \| \ | \ / \ | \| \ / \
| $$$$$$$\| $$$$$$$$| $$ | $$| $$$$$$\ \$$$$$$| $$\ / $$
| $$__| $$| $$__ | $$ | $$| $$___\$$ | $$ | $$$\ / $$$
| $$ $$| $$ \ \$$\ / $$ \$$ \ | $$ | $$$$\ $$$$
| $$$$$$$\| $$$$$ \$$\ $$ _\$$$$$$\ | $$ | $$\$$ $$ $$
| $$ | $$| $$_____ \$$ $$ | \__| $$ _| $$_ | $$ \$$$| $$
| $$ | $$| $$ \ \$$$ \$$ $$| $$ \| $$ \$ | $$
\$$ \$$ \$$$$$$$$ \$ \$$$$$$ \$$$$$$ \$$ \$$
{} | 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!("Revsim 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_green().bold().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.yellow().bold().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 : 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(&format!("Latest Client Version: {}", latest_client_version_result.bright_blue()));
latest_client_version = latest_client_version_result;
},
Err(e) => {
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(&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(&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);
}
}
}
}
// Wait for the latest client version to be fetched
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(&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");
// Download the latest bootstrapper
download_file(&http_client, &format!("https://{}/{}-{}", setup_url, latest_client_version, bootstrapper_filename), &latest_bootstrapper_path).await;
}
// Lets compare the SHA1 hash of the latest bootstrapper to the one we are currently running
// If they are the same, then we can continue with the update process
// We do this because antivirus software does not like this type of behavior
let latest_bootstrapper_hash = get_sha1_hash_of_file(&latest_bootstrapper_path).await;
let current_exe_hash = get_sha1_hash_of_file(&current_exe_path).await;
debug(&format!("Latest Bootstrapper Hash: {}", latest_bootstrapper_hash.bright_blue()));
debug(&format!("Current Bootstrapper Hash: {}", current_exe_hash.bright_blue()));
if latest_bootstrapper_hash != current_exe_hash {
info("Starting latest bootstrapper");
// 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.clone());
command.args(&args[1..]);
match command.spawn() {
Ok(_) => {},
Err(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;
command.spawn().expect("Bootstrapper is still corrupted.");
std::thread::sleep(std::time::Duration::from_secs(20));
}
}
}
#[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();
let desktop_file_content = &format!("[Desktop Entry]
Name=REVSIM Launcher
Exec={} %u
Icon={}
Type=Application
Terminal=true
Version={}
MimeType=x-scheme-handler/revsim-player;", latest_bootstrapper_path.to_str().unwrap(), latest_bootstrapper_path.to_str().unwrap(), env!("CARGO_PKG_VERSION"));
let desktop_file_path = dirs::data_local_dir().unwrap().join("applications").join("revsim-player.desktop");
std::fs::write(desktop_file_path, desktop_file_content).unwrap();
info("Please launch Revsim from the website, to continue with the update process.");
std::thread::sleep(std::time::Duration::from_secs(20));
}
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() { //|| !client_executable_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 != latest_bootstrapper_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 Client2016Zip : PathBuf = download_file_prefix(&http_client, format!("{}2016client.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
let Client2018Zip : PathBuf = download_file_prefix(&http_client, format!("{}2018client.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
let Client2021Zip : PathBuf = download_file_prefix(&http_client, format!("{}2021client.zip", VersionURLPrefix).as_str(), &temp_downloads_directory).await;
info("Download finished, extracting files.");
fn extract_to_dir( zip_file : &PathBuf, target_dir : &PathBuf ) {
info(format!("Extracting {} to {}", zip_file.to_str().unwrap().bright_blue(), target_dir.to_str().unwrap().bright_blue()).as_str());
let zip_file_cursor = std::fs::File::open(zip_file).unwrap();
zip_extract::extract(zip_file_cursor, target_dir, false).unwrap();
}
let client_2016_directory = current_version_directory.join("Client2016");
create_folder_if_not_exists(&client_2016_directory).await;
extract_to_dir(&Client2016Zip, &client_2016_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_2021_directory = current_version_directory.join("Client2021");
create_folder_if_not_exists(&client_2021_directory).await;
extract_to_dir(&Client2021Zip, &client_2021_directory);
info("Finished extracting files, cleaning up.");
std::fs::remove_dir_all(&temp_downloads_directory).unwrap();
let client_2016_app_settings = client_2016_directory.join("AppSettings.xml");
let client_2018_app_settings = client_2018_directory.join("AppSettings.xml");
if let Ok(xml_content) = http_get(&http_client, "https://clients.orcbin.com/AppSettings.xml").await {
let _ = std::fs::write(client_2016_app_settings, &xml_content);
let _ = std::fs::write(client_2018_app_settings, &xml_content);
}
// Install the revsim-player scheme in the registry
info("Installing revsim-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("revsim-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\"", latest_bootstrapper_path.to_str().unwrap())).unwrap();
defaulticon.set_value("", &format!("\"{}\",0", latest_bootstrapper_path.to_str().unwrap())).unwrap();
hkey_syntax_player.set_value("", &format!("URL: Revsim Protocol")).unwrap();
hkey_syntax_player.set_value("URL Protocol", &"").unwrap();
}
#[cfg(not(target_os = "windows"))]
{
// Linux support
let desktop_file_content = &format!("[Desktop Entry]
Name=REVSIM Launcher
Exec={} %u
Icon={}
Type=Application
Terminal=true
Version={}
MimeType=x-scheme-handler/revsim-player;", latest_bootstrapper_path.to_str().unwrap(), latest_bootstrapper_path.to_str().unwrap(), env!("CARGO_PKG_VERSION"));
let desktop_file_path = dirs::data_local_dir().unwrap().join("applications").join("revsim-player.desktop");
std::fs::write(desktop_file_path, desktop_file_content).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 "revsim-player://1+launchmode:play+gameinfo:TICKET+placelauncherurl:https://revsim.xyz/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://revsim.xyz/games").spawn().unwrap();
std::process::exit(0);
}
#[cfg(not(target_os = "windows"))]
{
std::process::Command::new("xdg-open").arg("https://revsim.xyz/games").spawn().unwrap();
std::process::exit(0);
}
}
let main_args = &args[1];
let main_args = main_args.replace("revsim-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();
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(&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();
},
_ => {}
}
}
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());
}
}
let client_executable_path : PathBuf;
debug(&client_year.to_string());
if client_year == "2018" {
client_executable_path = current_version_directory.join("Client2018").join("RevsimPlayerBeta.exe");
} else if client_year == "2021" {
client_executable_path = current_version_directory.join("Client2021").join("RevsimPlayerBeta.exe");
} else {
client_executable_path = current_version_directory.join("Client2016").join("RevsimPlayerBeta.exe");
}
if !client_executable_path.exists() {
// Delete AppSettings.xml so the bootstrapper will download the client again
let app_settings_path = current_version_directory.join("AppSettings.xml");
std::fs::remove_file(app_settings_path).unwrap();
error("Failed to run RevsimPlayerBeta.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 REVSIM");
#[cfg(target_os = "windows")]
{
let mut command = std::process::Command::new(client_executable_path);
if client_year == "2015" {
// Use the 2015 format with -a, -j, -t arguments
command.args(&[
"-a", format!("http://{}/Login/Negotiate.ashx", base_url).as_str(),
"-j", join_script.as_str(),
"-t", authentication_ticket.as_str()
]);
} else {
// Use the standard format for all other clients
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);
if client_year == "2015" {
// Use the 2015 format with -a, -j, -t arguments for wine too
command.args(&[
client_executable_path.to_str().unwrap(),
"-a", format!("http://{}/Login/Negotiate.ashx", base_url).as_str(),
"-j", join_script.as_str(),
"-t", authentication_ticket.as_str()
]);
} else {
// Use the standard format for all other clients
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.");
std::thread::sleep(std::time::Duration::from_secs(10));
std::process::exit(0);
}
}
}
BIN
View File
Binary file not shown.
+11
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 RevsimPlayerLauncher.exe from syntax_bootstrapper.exe
ren target\i686-pc-windows-msvc\release\syntax_bootstrapper.exe RevsimPlayerLauncher.exe