Initial commit
This commit is contained in:
commit
eff3bccf2b
11 changed files with 5287 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
4873
Cargo.lock
generated
Normal file
4873
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "ZZZ-RR-Launcher"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
eframe = "0.31.1"
|
||||
git2 = "0.20.1"
|
||||
reqwest = {version = "0.12.15", features = ["blocking", "rustls-tls"] }
|
3
src/core/mod.rs
Normal file
3
src/core/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
// Fuck Hadros
|
||||
pub mod zenless;
|
||||
pub mod rustdl;
|
46
src/core/rustdl.rs
Normal file
46
src/core/rustdl.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Import std crate
|
||||
use std::process::Command;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
// Funtion for Downloading and installing rust automatically
|
||||
pub fn download_install_rust() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let url = "https://win.rustup.rs/x86_64";
|
||||
let exe_name = "rustup-init.exe";
|
||||
|
||||
let response = reqwest::blocking::get(url)?;
|
||||
let bytes = response.bytes()?;
|
||||
|
||||
fs::write(exe_name, &bytes)?;
|
||||
println!("[✓] Download finished: {}", exe_name);
|
||||
|
||||
let status = Command::new(exe_name)
|
||||
.args(["-y", "--default-toolchain", "nightly"])
|
||||
.status()?;
|
||||
|
||||
if status.success() {
|
||||
println!("[✓] Rust nightly installed!");
|
||||
} else {
|
||||
eprintln!("[✗] Installation has failed.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Downloading and Install Windows MSVC for building the PS
|
||||
let status_stable = Command::new("rustup")
|
||||
.args(["install", "stable-x86_64-pc-windows-msvc"])
|
||||
.status()?;
|
||||
|
||||
if status_stable.success() {
|
||||
println!("[✓] Rust stable toolchain installed for x86_64-pc-windows-msvc!");
|
||||
} else {
|
||||
eprintln!("[✗] Failed to install stable MSVC toolchain.");
|
||||
}
|
||||
|
||||
// Cleaning
|
||||
if Path::new(exe_name).exists() {
|
||||
fs::remove_file(exe_name)?;
|
||||
println!("[✓] Cleanup finished.");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
2
src/core/zenless/mod.rs
Normal file
2
src/core/zenless/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod zenless_srv;
|
||||
pub mod zenless_client_patch;
|
93
src/core/zenless/zenless_client_patch.rs
Normal file
93
src/core/zenless/zenless_client_patch.rs
Normal file
|
@ -0,0 +1,93 @@
|
|||
// Import std and git2 crate for downloading, building and launching client part
|
||||
use std::process::{Command, Stdio};
|
||||
use std::fs::File;
|
||||
use std::{fs, path::Path, path::PathBuf};
|
||||
use git2::Repository;
|
||||
|
||||
// download client patch from the distant git repository
|
||||
pub fn zzz_download_client_patch() {
|
||||
|
||||
let zzz_client_dir_path = "zzz_client_patch";
|
||||
if let Err(e) = fs::create_dir_all(zzz_client_dir_path) {
|
||||
eprintln!("Error creating folder: {}", e);
|
||||
return;
|
||||
}
|
||||
|
||||
let zzz_game_dir_path = "zzz_game";
|
||||
if let Err(e) = fs::create_dir_all(zzz_game_dir_path) {
|
||||
eprintln!("Error creating folder: {}", e);
|
||||
return;
|
||||
}
|
||||
|
||||
let repo_url = "https://git.xeondev.com/traffic95/trigger-patch";
|
||||
|
||||
let path = Path::new(zzz_client_dir_path);
|
||||
|
||||
match Repository::clone(repo_url, path) {
|
||||
Ok(_) => {
|
||||
println!("✅ Client Repo cloned at {}", zzz_client_dir_path);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Error during Client Patch downloading : {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build the Client from source
|
||||
pub fn zzz_build_client_patch() {
|
||||
|
||||
let client_patch_log_file_path = "client_patch_log.txt";
|
||||
let client_patch_log_file = File::create(client_patch_log_file_path).expect("Unable to create log file");
|
||||
let client_patch_lof_file_clone = client_patch_log_file.try_clone().expect("Unable to clone log file handle");
|
||||
let mut child = Command::new("cargo")
|
||||
.arg("+nightly")
|
||||
.arg("build")
|
||||
.arg("--target")
|
||||
.arg("x86_64-pc-windows-msvc")
|
||||
.current_dir("zzz_client_patch")
|
||||
.stdout(Stdio::from(client_patch_log_file))
|
||||
.stderr(Stdio::from(client_patch_lof_file_clone))
|
||||
.spawn()
|
||||
.expect("❌ Error during the launch of cargo build");
|
||||
|
||||
let status = child.wait().expect("Error during process waiting");
|
||||
|
||||
if status.success() {
|
||||
println!("✅ Client built!");
|
||||
} else {
|
||||
println!("❌ Build failed with status: {}", status);
|
||||
}
|
||||
}
|
||||
|
||||
// Move the exe and dll of the Client patch to the game folder, and launch it
|
||||
pub fn zzz_client_patch_loaded() {
|
||||
let source_dir = PathBuf::from("zzz_client_patch/target/x86_64-pc-windows-msvc/debug");
|
||||
let launcher = source_dir.join("launcher.exe");
|
||||
let dll = source_dir.join("trigger.dll");
|
||||
|
||||
let dest_dir = PathBuf::from("zzz_game");
|
||||
|
||||
fs::copy(&launcher, dest_dir.join("launcher.exe")).expect("Failed to copy launcher.exe");
|
||||
fs::copy(&dll, dest_dir.join("trigger.dll")).expect("Failed to copy trigger.dll");
|
||||
|
||||
let launcher_exec_path = PathBuf::from("zzz_game/launcher.exe");
|
||||
|
||||
if !launcher_exec_path.exists() {
|
||||
eprintln!("❌ launcher.exe not found, Make sure the build was successful and the files were copied.");
|
||||
return;
|
||||
}
|
||||
|
||||
match Command::new(&launcher_exec_path)
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit())
|
||||
.spawn()
|
||||
{
|
||||
Ok(mut child) => {
|
||||
println!("launcher.exe is starting");
|
||||
let _ = child.wait();
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ Can't Launch the launcher : {}",e )
|
||||
}
|
||||
}
|
||||
}
|
119
src/core/zenless/zenless_srv.rs
Normal file
119
src/core/zenless/zenless_srv.rs
Normal file
|
@ -0,0 +1,119 @@
|
|||
// Import std and git2 crate for downloading, building and launching servers part
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs::{self, File},
|
||||
io::{BufRead, BufReader},
|
||||
path::Path,
|
||||
process::{Command, Stdio},
|
||||
sync::{Arc, Mutex},
|
||||
thread,
|
||||
};
|
||||
|
||||
use git2::Repository;
|
||||
|
||||
// Funtion for download the server from the distant Git repository
|
||||
pub fn zzz_download_server() {
|
||||
let zzz_dir_path = "zzz_ps";
|
||||
if let Err(e) = fs::create_dir_all(zzz_dir_path) {
|
||||
eprintln!("Error creating folder: {}", e);
|
||||
return;
|
||||
}
|
||||
|
||||
let repo_url = "https://git.xeondev.com/traffic95/trigger-rs";
|
||||
let path = Path::new(zzz_dir_path);
|
||||
|
||||
match Repository::clone(repo_url, path) {
|
||||
Ok(_) => println!("✅ Server Repo cloned at {}", zzz_dir_path),
|
||||
Err(e) => eprintln!("❌ Clone failed: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
// Function for building the server via cargo command build
|
||||
pub fn zzz_build_server() {
|
||||
let log_path = "server_log.txt";
|
||||
let log_file = File::create(log_path).expect("Cannot create log file");
|
||||
let log_file_clone = log_file.try_clone().expect("Cannot clone log handle");
|
||||
|
||||
let mut child = Command::new("cargo")
|
||||
.arg("build")
|
||||
.current_dir("zzz_ps")
|
||||
.stdout(Stdio::from(log_file))
|
||||
.stderr(Stdio::from(log_file_clone))
|
||||
.spawn()
|
||||
.expect("Error launching build");
|
||||
|
||||
let status = child.wait().expect("Error waiting for process");
|
||||
|
||||
if status.success() {
|
||||
println!("✅ Server built!");
|
||||
} else {
|
||||
println!("❌ Build failed with status: {}", status);
|
||||
}
|
||||
}
|
||||
|
||||
// List every servers part in the Launcher UI
|
||||
pub fn list_available_servers() -> Vec<String> {
|
||||
let server_dir = Path::new("zzz_ps/target/debug");
|
||||
let expected = [
|
||||
"trigger-muip-server.exe",
|
||||
"trigger-hall-server.exe",
|
||||
"trigger-gate-server.exe",
|
||||
"trigger-game-server.exe",
|
||||
"trigger-dispatch-server.exe",
|
||||
"trigger-battle-server.exe",
|
||||
];
|
||||
|
||||
expected
|
||||
.iter()
|
||||
.filter_map(|name| {
|
||||
let path = server_dir.join(name);
|
||||
if path.exists() {
|
||||
Some(name.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
// Launch the servers inside of every designed tabs with logs
|
||||
pub fn launch_server_with_logs(
|
||||
exe_name: &str,
|
||||
logs: Arc<Mutex<HashMap<String, Vec<String>>>>,
|
||||
) {
|
||||
let path = format!("zzz_ps/target/debug/{}", exe_name);
|
||||
let mut child = match Command::new(&path)
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
{
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
eprintln!("❌ Failed to launch {}: {}", exe_name, e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let stdout = child.stdout.take().unwrap();
|
||||
let stderr = child.stderr.take().unwrap();
|
||||
|
||||
let logs_clone = Arc::clone(&logs);
|
||||
let name = exe_name.to_string();
|
||||
thread::spawn(move || {
|
||||
let reader = BufReader::new(stdout);
|
||||
for line in reader.lines().flatten() {
|
||||
let mut logs = logs_clone.lock().unwrap();
|
||||
logs.entry(name.clone()).or_default().push(format!("📤 {}", line));
|
||||
}
|
||||
});
|
||||
|
||||
let logs_clone = Arc::clone(&logs);
|
||||
let name = exe_name.to_string();
|
||||
thread::spawn(move || {
|
||||
let reader = BufReader::new(stderr);
|
||||
for line in reader.lines().flatten() {
|
||||
let mut logs = logs_clone.lock().unwrap();
|
||||
logs.entry(name.clone()).or_default().push(format!("📕 {}", line));
|
||||
}
|
||||
});
|
||||
}
|
17
src/main.rs
Normal file
17
src/main.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Import custom rust file as crate
|
||||
mod ui;
|
||||
mod core;
|
||||
use ui::gui::ZZZRRLauncher;
|
||||
|
||||
// Import eframe crate to make the UI
|
||||
use eframe::NativeOptions;
|
||||
|
||||
// Init the Eframe UI and Launch the launcher
|
||||
fn main() -> eframe::Result<()> {
|
||||
let options = NativeOptions::default();
|
||||
eframe::run_native(
|
||||
"ZZZ-RR-Launcher Beta",
|
||||
options,
|
||||
Box::new(|_cc| Ok(Box::new(ZZZRRLauncher::default()))),
|
||||
)
|
||||
}
|
122
src/ui/gui.rs
Normal file
122
src/ui/gui.rs
Normal file
|
@ -0,0 +1,122 @@
|
|||
// Import "std crate" with necessary depedencies for buttons, "Eframe" for UI, And everything needed for the buttons funtions
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use eframe::egui;
|
||||
use crate::core::{rustdl, zenless::zenless_client_patch, zenless::zenless_srv};
|
||||
|
||||
|
||||
// Define the Launcher structures and functionnalities
|
||||
pub struct ZZZRRLauncher {
|
||||
selected_tab: Tab,
|
||||
available_servers: Vec<String>,
|
||||
logs: Arc<Mutex<HashMap<String, Vec<String>>>>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum Tab {
|
||||
Download,
|
||||
Server,
|
||||
Zzz,
|
||||
ServerInstance(String),
|
||||
}
|
||||
|
||||
impl Default for ZZZRRLauncher {
|
||||
fn default() -> Self {
|
||||
let logs = Arc::new(Mutex::new(HashMap::new()));
|
||||
let available_servers = zenless_srv::list_available_servers();
|
||||
|
||||
Self {
|
||||
selected_tab: Tab::Download,
|
||||
available_servers,
|
||||
logs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Generate an UI with some buttons, each buttons are different
|
||||
impl eframe::App for ZZZRRLauncher {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
if ui.selectable_label(self.selected_tab == Tab::Download, "📥 Download").clicked() {
|
||||
self.selected_tab = Tab::Download;
|
||||
}
|
||||
if ui.selectable_label(self.selected_tab == Tab::Server, "🛠 Server").clicked() {
|
||||
self.selected_tab = Tab::Server;
|
||||
}
|
||||
if ui.selectable_label(self.selected_tab == Tab::Zzz, "🎮 ZZZ").clicked() {
|
||||
self.selected_tab = Tab::Zzz;
|
||||
}
|
||||
|
||||
for server in &self.available_servers {
|
||||
if ui
|
||||
.selectable_label(
|
||||
self.selected_tab == Tab::ServerInstance(server.clone()),
|
||||
format!("🔌 {}", server),
|
||||
)
|
||||
.clicked()
|
||||
{
|
||||
self.selected_tab = Tab::ServerInstance(server.clone());
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
match &self.selected_tab {
|
||||
Tab::Download => {
|
||||
ui.heading("Download Tools");
|
||||
if ui.button("Download Server").clicked() {
|
||||
zenless_srv::zzz_download_server();
|
||||
self.available_servers = zenless_srv::list_available_servers();
|
||||
}
|
||||
if ui.button("Download Client Patch").clicked() {
|
||||
zenless_client_patch::zzz_download_client_patch();
|
||||
}
|
||||
if ui.button("Download Rust Nightly").clicked() {
|
||||
let _ = rustdl::download_install_rust();
|
||||
}
|
||||
}
|
||||
Tab::Server => {
|
||||
ui.heading("Server Management");
|
||||
if ui.button("Build Server").clicked() {
|
||||
zenless_srv::zzz_build_server();
|
||||
self.available_servers = zenless_srv::list_available_servers();
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
for server in &self.available_servers {
|
||||
if ui.button(format!("🚀 Launch {}", server)).clicked() {
|
||||
zenless_srv::launch_server_with_logs(server, Arc::clone(&self.logs));
|
||||
self.selected_tab = Tab::ServerInstance(server.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
Tab::Zzz => {
|
||||
ui.heading("ZZZ Client Patch");
|
||||
if ui.button("Build Client Patch").clicked() {
|
||||
zenless_client_patch::zzz_build_client_patch();
|
||||
}
|
||||
if ui.button("Launch ZZZ").clicked() {
|
||||
zenless_client_patch::zzz_client_patch_loaded();
|
||||
}
|
||||
}
|
||||
Tab::ServerInstance(server_name) => {
|
||||
ui.heading(format!("📡 Logs for {}", server_name));
|
||||
if let Some(lines) = self.logs.lock().unwrap().get(server_name) {
|
||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
for line in lines.iter().rev().take(100).rev() {
|
||||
ui.monospace(line);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ui.label("No logs yet.");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
2
src/ui/mod.rs
Normal file
2
src/ui/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
// Fuck Hadros
|
||||
pub mod gui;
|
Loading…
Reference in a new issue