Skip to content

Commit 17630c3

Browse files
committed
refactor rust code
1 parent 48a43be commit 17630c3

File tree

14 files changed

+924
-953
lines changed

14 files changed

+924
-953
lines changed

src-tauri/src/background_thread.rs

Lines changed: 0 additions & 39 deletions
This file was deleted.

src-tauri/src/cli.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
use crate::{constants::*, errors::*, validation};
2+
use gumdrop::Options;
3+
use std::process::exit;
4+
5+
#[derive(Debug, Options)]
6+
pub struct CliArgs {
7+
#[options(no_short, help = "print help message")]
8+
pub help: bool,
9+
10+
#[options(help = "target server IP address")]
11+
pub host: Option<String>,
12+
13+
#[options(help = "target server port")]
14+
pub port: Option<i32>,
15+
16+
#[options(help = "target server password")]
17+
pub password: Option<String>,
18+
19+
#[options(help = "nickname to join server with")]
20+
pub name: Option<String>,
21+
22+
#[options(help = "game path to use for both game executable and samp.dll")]
23+
pub gamepath: Option<String>,
24+
}
25+
26+
impl CliArgs {
27+
pub fn validate(&self) -> Result<()> {
28+
if let Some(ref host) = self.host {
29+
validation::validate_hostname(host)?;
30+
}
31+
32+
if let Some(port) = self.port {
33+
validation::validate_port(port)?;
34+
}
35+
36+
if let Some(ref name) = self.name {
37+
validation::validate_player_name(name)?;
38+
}
39+
40+
if let Some(ref gamepath) = self.gamepath {
41+
validation::validate_file_path(gamepath)?;
42+
43+
let gta_exe = format!("{}/{}", gamepath, GTA_SA_EXECUTABLE);
44+
if !std::path::Path::new(&gta_exe).exists() {
45+
return Err(LauncherError::NotFound(
46+
format!("GTA San Andreas executable not found at: {}", gta_exe)
47+
));
48+
}
49+
}
50+
51+
Ok(())
52+
}
53+
54+
pub fn print_help_and_exit(program_name: &str) -> ! {
55+
// Using println! here is appropriate for CLI help output
56+
println!(
57+
"Open Multiplayer Launcher
58+
59+
Usage: {} [OPTIONS]
60+
61+
Options:
62+
--help
63+
-h, --host <HOST> Server IP
64+
-p, --port <PORT> Server port
65+
-P, --password <PASSWORD> Server password
66+
-n, --name <NAME> Nickname
67+
-g, --gamepath <GAMEPATH> Game path
68+
",
69+
program_name
70+
);
71+
exit(0)
72+
}
73+
74+
pub fn has_game_launch_args(&self) -> bool {
75+
self.host.is_some() && self.name.is_some() && self.port.is_some() && self.gamepath.is_some()
76+
}
77+
78+
pub fn get_password(&self) -> String {
79+
match &self.password {
80+
Some(pwd) => validation::sanitize_password(pwd),
81+
None => String::new(),
82+
}
83+
}
84+
}

src-tauri/src/commands.rs

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{background_thread::check_for_new_instance_and_close, injector, samp};
1+
use crate::{injector, samp};
22
use log::info;
33

44
#[tauri::command]
@@ -11,8 +11,10 @@ pub async fn inject(
1111
omp_file: &str,
1212
password: &str,
1313
discord: bool,
14-
) -> Result<(), String> {
15-
injector::run_samp(name, ip, port, exe, dll, omp_file, password, discord).await
14+
) -> std::result::Result<(), String> {
15+
injector::run_samp(name, ip, port, exe, dll, omp_file, password, discord)
16+
.await
17+
.map_err(|e| e.to_string())
1618
}
1719

1820
#[tauri::command]
@@ -26,17 +28,19 @@ pub fn get_nickname_from_samp() -> String {
2628
}
2729

2830
#[tauri::command]
29-
pub fn rerun_as_admin() -> Result<String, String> {
30-
let res = std::env::current_exe();
31-
match res {
32-
Ok(p) => {
33-
let path = p.into_os_string().into_string().unwrap();
34-
runas::Command::new(path).arg("").status().unwrap();
35-
check_for_new_instance_and_close();
36-
Ok("SUCCESS".to_string())
37-
}
38-
Err(_) => Err("FAILED".to_string()),
39-
}
31+
pub fn rerun_as_admin() -> std::result::Result<String, String> {
32+
let exe_path = std::env::current_exe()
33+
.map_err(|_| "Failed to get current executable path".to_string())?
34+
.into_os_string()
35+
.into_string()
36+
.map_err(|_| "Failed to convert path to string".to_string())?;
37+
38+
runas::Command::new(exe_path)
39+
.arg("")
40+
.status()
41+
.map_err(|_| "Failed to restart as administrator".to_string())?;
42+
43+
Ok("SUCCESS".to_string())
4044
}
4145

4246
#[tauri::command]
@@ -45,21 +49,25 @@ pub fn get_samp_favorite_list() -> String {
4549
}
4650

4751
#[tauri::command]
48-
pub fn resolve_hostname(hostname: String) -> Result<String, String> {
52+
pub fn resolve_hostname(hostname: String) -> std::result::Result<String, String> {
4953
use std::net::{IpAddr, ToSocketAddrs};
5054

55+
if hostname.is_empty() {
56+
return Err("Hostname cannot be empty".to_string());
57+
}
58+
5159
let addr = format!("{}:80", hostname);
52-
match addr.to_socket_addrs() {
53-
Ok(addrs) => {
54-
for ip in addrs {
55-
if let IpAddr::V4(ipv4) = ip.ip() {
56-
return Ok(ipv4.to_string());
57-
}
58-
}
59-
Err("No IPv4 address found".to_string())
60+
let addrs = addr
61+
.to_socket_addrs()
62+
.map_err(|e| format!("Failed to resolve hostname '{}': {}", hostname, e))?;
63+
64+
for ip in addrs {
65+
if let IpAddr::V4(ipv4) = ip.ip() {
66+
return Ok(ipv4.to_string());
6067
}
61-
Err(e) => Err(format!("Failed to resolve: {}", e)),
6268
}
69+
70+
Err(format!("No IPv4 address found for hostname '{}'", hostname))
6371
}
6472

6573
#[tauri::command]
@@ -80,7 +88,5 @@ pub fn is_process_alive(pid: u32) -> bool {
8088

8189
#[tauri::command]
8290
pub fn log(msg: &str) -> () {
83-
info!("{}", msg);
84-
println!("{}", msg);
91+
info!("Frontend log: {}", msg);
8592
}
86-

src-tauri/src/constants.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
pub const IPC_PORT: u16 = 45791;
2+
pub const RPC_PORT: u16 = 46290;
3+
4+
pub const MAX_HOSTNAME_LENGTH: u32 = 63;
5+
pub const MAX_GAMEMODE_LENGTH: u32 = 39;
6+
pub const MAX_LANGUAGE_LENGTH: u32 = 39;
7+
pub const MAX_DISCORD_LINK_LENGTH: u32 = 50;
8+
pub const MAX_BANNER_URL_LENGTH: u32 = 160;
9+
pub const MAX_LOGO_URL_LENGTH: u32 = 160;
10+
11+
pub const MAX_PLAYER_COUNT: u16 = 1000;
12+
pub const MAX_RULE_COUNT: u16 = 1000;
13+
14+
pub const QUERY_TIMEOUT_SECS: u64 = 2;
15+
pub const OMP_EXTRA_INFO_UPDATE_COOLDOWN_SECS: u64 = 3;
16+
17+
pub const INJECTION_MAX_RETRIES: u32 = 5;
18+
pub const INJECTION_RETRY_DELAY_MS: u64 = 500;
19+
20+
pub const UDP_BUFFER_SIZE: usize = 1500;
21+
pub const PROCESS_MODULE_BUFFER_SIZE: usize = 1024;
22+
23+
pub const SAMP_PACKET_HEADER: &[u8] = b"SAMP";
24+
25+
pub const QUERY_TYPE_INFO: char = 'i';
26+
pub const QUERY_TYPE_PLAYERS: char = 'c';
27+
pub const QUERY_TYPE_RULES: char = 'r';
28+
pub const QUERY_TYPE_EXTRA_INFO: char = 'o';
29+
pub const QUERY_TYPE_PING: char = 'p';
30+
31+
pub const PING_TIMEOUT: u32 = 9999;
32+
33+
pub const LOG_FILE_NAME: &str = "omp-launcher.log";
34+
pub const DATA_DIR_NAME: &str = "mp.open.launcher";
35+
36+
pub const GTA_SA_EXECUTABLE: &str = "gta_sa.exe";
37+
pub const SAMP_DLL: &str = "samp.dll";
38+
pub const OMP_CLIENT_DLL: &str = "omp-client.dll";
39+
40+
pub const DEEPLINK_SCHEME_OMP: &str = "omp";
41+
pub const DEEPLINK_SCHEME_SAMP: &str = "samp";
42+
pub const DEEPLINK_IDENTIFIER: &str = "mp.open.launcher";
43+
44+
pub const WINDOW_MIN_WIDTH: u32 = 1000;
45+
pub const WINDOW_MIN_HEIGHT: u32 = 700;
46+
47+
#[cfg(target_os = "windows")]
48+
pub const SAMP_REGISTRY_KEY: &str = r"Software\SAMP";
49+
50+
#[cfg(target_os = "windows")]
51+
pub const SAMP_USERDATA_PATH: &str = r"\GTA San Andreas User Files\SAMP\USERDATA.DAT";
52+
53+
pub const ERROR_DIRECTORY_EXISTS: i32 = 183;
54+
pub const ERROR_ACCESS_DENIED: i32 = 5;
55+
pub const ERROR_ELEVATION_REQUIRED: i32 = 740;

src-tauri/src/errors.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use std::fmt;
2+
3+
#[derive(Debug)]
4+
pub enum LauncherError {
5+
Io(std::io::Error),
6+
SerdeJson(serde_json::Error),
7+
SystemTime(std::time::SystemTimeError),
8+
Parse(String),
9+
Network(String),
10+
Process(String),
11+
Injection(String),
12+
Registry(String),
13+
Storage(String),
14+
InvalidInput(String),
15+
NotFound(String),
16+
AccessDenied(String),
17+
InternalError(String),
18+
}
19+
20+
impl fmt::Display for LauncherError {
21+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22+
match self {
23+
LauncherError::Io(err) => write!(f, "IO error: {}", err),
24+
LauncherError::SerdeJson(err) => write!(f, "JSON error: {}", err),
25+
LauncherError::SystemTime(err) => write!(f, "System time error: {}", err),
26+
LauncherError::Parse(msg) => write!(f, "Parse error: {}", msg),
27+
LauncherError::Network(msg) => write!(f, "Network error: {}", msg),
28+
LauncherError::Process(msg) => write!(f, "Process error: {}", msg),
29+
LauncherError::Injection(msg) => write!(f, "Injection error: {}", msg),
30+
LauncherError::Registry(msg) => write!(f, "Registry error: {}", msg),
31+
LauncherError::Storage(msg) => write!(f, "Storage error: {}", msg),
32+
LauncherError::InvalidInput(msg) => write!(f, "Invalid input: {}", msg),
33+
LauncherError::NotFound(msg) => write!(f, "Not found: {}", msg),
34+
LauncherError::AccessDenied(msg) => write!(f, "Access denied - administrator privileges required: {}", msg),
35+
LauncherError::InternalError(msg) => write!(f, "Internal error: {}", msg),
36+
}
37+
}
38+
}
39+
40+
impl std::error::Error for LauncherError {}
41+
42+
impl From<std::io::Error> for LauncherError {
43+
fn from(err: std::io::Error) -> Self {
44+
match err.raw_os_error() {
45+
Some(5) => LauncherError::AccessDenied("Access denied".to_string()),
46+
Some(740) => LauncherError::AccessDenied("Admin privileges required".to_string()),
47+
_ => LauncherError::Io(err),
48+
}
49+
}
50+
}
51+
52+
impl From<serde_json::Error> for LauncherError {
53+
fn from(err: serde_json::Error) -> Self {
54+
LauncherError::SerdeJson(err)
55+
}
56+
}
57+
58+
impl From<std::time::SystemTimeError> for LauncherError {
59+
fn from(err: std::time::SystemTimeError) -> Self {
60+
LauncherError::SystemTime(err)
61+
}
62+
}
63+
64+
impl From<String> for LauncherError {
65+
fn from(msg: String) -> Self {
66+
LauncherError::InternalError(msg)
67+
}
68+
}
69+
70+
impl From<LauncherError> for String {
71+
fn from(err: LauncherError) -> Self {
72+
match err {
73+
LauncherError::AccessDenied(_) => "need_admin".to_string(),
74+
_ => err.to_string(),
75+
}
76+
}
77+
}
78+
79+
pub type Result<T> = std::result::Result<T, LauncherError>;
80+
81+
impl From<LauncherError> for tauri::InvokeError {
82+
fn from(err: LauncherError) -> Self {
83+
tauri::InvokeError::from(err.to_string())
84+
}
85+
}

0 commit comments

Comments
 (0)