From e38007e5a4bc9dbc1f87539706252e621b9bafb7 Mon Sep 17 00:00:00 2001 From: Zefanja Jobse Date: Sat, 14 Jan 2023 22:34:18 +0100 Subject: [PATCH 1/2] add player check --- config.txt | 6 +- src/actions/backend.rs | 120 +++++++++++++++++++++++++++++++--- src/actions/game.rs | 78 +++++++++++++++------- src/actions/launchers.rs | 63 +++++++++--------- src/actions/mod.rs | 2 +- src/ea_desktop_test.rs | 27 ++++---- src/functions/anti_afk.rs | 2 +- src/functions/auto_message.rs | 15 ++++- src/functions/mod.rs | 2 +- src/functions/seed_server.rs | 105 +++++++++++++++++++++-------- src/input/chars.rs | 22 +++---- src/input/mod.rs | 2 +- src/input/send_keys.rs | 12 ++-- src/main.rs | 45 +++++++------ src/origin_test.rs | 27 ++++---- src/structs.rs | 55 ++++++++++++---- 16 files changed, 409 insertions(+), 174 deletions(-) diff --git a/config.txt b/config.txt index 49878d1..25a1f2f 100644 --- a/config.txt +++ b/config.txt @@ -1,5 +1,5 @@ -group_id = '0fda8e4c-5be3-11eb-b1da-cd4ff7dab605' -game_location = 'C:\Program Files (x86)\Origin Games\Battlefield 1\bf1.exe' +group_id = 'eacd7a78-8887-11ed-8401-02420a00099d' +game_location = 'E:\Program Files\Origin Games\Battlefield 1\bf1.exe' hostname = 'Jobse-PC' allow_shutdown = false send_messages = false @@ -12,3 +12,5 @@ message_start_time_utc = '12:00' message_stop_time_utc = '23:00' message_timeout_mins = 8 game = 'Bf1' +seeder_name = 'iiTzArcur' +find_player_max_retries = 10 diff --git a/src/actions/backend.rs b/src/actions/backend.rs index 0481ff8..7c1c5c4 100644 --- a/src/actions/backend.rs +++ b/src/actions/backend.rs @@ -1,20 +1,124 @@ -use std::time::Duration; -use std::sync::{atomic, Arc}; -use std::sync::atomic::AtomicU32; use crate::structs; +use std::sync::atomic::AtomicU32; +use std::sync::{atomic, Arc}; +use std::time::Duration; -pub fn ping(cfg: &structs::SeederConfig, game_info: &structs::GameInfo, origin_info: &structs::GameInfo, retry_launch: &Arc) { - match ureq::post("https://manager-api.gametools.network/api/seederinfo").timeout(Duration::new(10, 0)).send_json( - ureq::json!({ +pub fn ping( + cfg: &structs::SeederConfig, + game_info: &structs::GameInfo, + origin_info: &structs::GameInfo, + retry_launch: &Arc, +) { + match ureq::post("https://manager-api.gametools.network/api/seederinfo") + .timeout(Duration::new(10, 0)) + .send_json(ureq::json!({ "groupid": cfg.group_id, "isrunning": game_info.is_running, "retrycount": retry_launch.load(atomic::Ordering::Relaxed), "hostname": cfg.hostname, "isoriginrunning": origin_info.is_running, "game": cfg.game.short_name(), - }), - ) { + })) { Ok(_) => {} Err(_) => log::error!("Couln't send update of client to backend"), } } + +pub fn has_player(cfg: &structs::SeederConfig, game_id: &str) -> bool { + if cfg.game == structs::Games::Bf4 { + return bf4_has_player(cfg, game_id); + } + bf1_has_player(cfg, game_id) +} + +pub fn bf1_has_player(cfg: &structs::SeederConfig, game_id: &str) -> bool { + let url = format!( + "https://api.gametools.network/bf1/players/?gameid={}", + game_id + ); + match ureq::get(&url[..]).call() { + Ok(response) => match response.into_json::() { + Ok(server_info) => { + // if valid timestamp + match chrono::NaiveDateTime::from_timestamp_millis( + server_info.update_timestamp * 1000, + ) { + Some(naive_time) => { + let timestamp_time = + chrono::DateTime::::from_utc(naive_time, chrono::Utc) + .time(); + let current = chrono::Utc::now().time(); + let diff = current - timestamp_time; + if diff.num_minutes() > 2 { + return true; + } + + if !cfg.seeder_name.is_empty() + && (server_info.teams[0].players.contains( + &structs::GametoolsServerPlayer { + name: cfg.seeder_name.clone(), + }, + ) || server_info.teams[1].players.contains( + &structs::GametoolsServerPlayer { + name: cfg.seeder_name.clone(), + }, + )) + { + return true; + } + false + } + None => { + log::error!("no timestamp in players?"); + log::info!("reconnecting..."); + true + } + } + } + Err(e) => { + log::error!("Failed to get info about server to join: {}", e); + log::info!("reconnecting..."); + true + } + }, + Err(e) => { + log::error!("Failed to connect to gametools: {}", e); + log::info!("reconnecting..."); + true + } + } +} + +pub fn bf4_has_player(cfg: &structs::SeederConfig, game_id: &str) -> bool { + let url = format!( + "https://api.gametools.network/bf4/detailedserver/?gameid={}&lang=en-us®ion=all&platform=pc&service=undefined&", + game_id + ); + match ureq::get(&url[..]).call() { + Ok(response) => match response.into_json::() { + Ok(server_info) => { + if !cfg.seeder_name.is_empty() + && !server_info + .players + .unwrap() + .contains(&structs::GametoolsServerPlayer { + name: cfg.seeder_name.clone(), + }) + { + return true; + } + false + } + Err(e) => { + log::error!("Failed to get info about server to join: {}", e); + log::info!("reconnecting..."); + true + } + }, + Err(e) => { + log::error!("Failed to connect to gametools: {}", e); + log::info!("reconnecting..."); + true + } + } +} diff --git a/src/actions/game.rs b/src/actions/game.rs index 69d1559..f42cf0a 100644 --- a/src/actions/game.rs +++ b/src/actions/game.rs @@ -1,21 +1,25 @@ +use crate::actions::launchers; +use crate::input::{ + chars::{char_to_dxcodes, DXCode}, + send_keys, +}; +use crate::structs; +use crate::structs::GameInfo; +use registry::{Hive, Security}; use std::ffi::OsStr; use std::iter::once; use std::os::windows::prelude::OsStrExt; use std::ptr; +use std::sync::atomic::AtomicU32; +use std::sync::{atomic, Arc}; use std::thread::sleep; use std::time::Duration; -use std::sync::{atomic, Arc}; -use std::sync::atomic::AtomicU32; -use registry::{Hive, Security}; use winapi::shared::windef::{HWND__, LPRECT, RECT}; +use winapi::um::winuser::IsIconic; use winapi::um::winuser::{ - FindWindowW, GetDesktopWindow, GetWindowRect, SetForegroundWindow, ShowWindow, GetForegroundWindow, SendMessageW + FindWindowW, GetDesktopWindow, GetForegroundWindow, GetWindowRect, SendMessageW, + SetForegroundWindow, ShowWindow, }; -use crate::actions::launchers; -use crate::input::{chars::{char_to_dxcodes, DXCode}, send_keys}; -use crate::structs; -use crate::structs::GameInfo; -use winapi::um::winuser::IsIconic; pub fn is_fullscreen(cfg: &structs::SeederConfig) -> bool { let game_info = is_running(cfg); @@ -42,19 +46,34 @@ pub fn is_fullscreen(cfg: &structs::SeederConfig) -> bool { } pub fn find_game(cfg: &structs::SeederConfig) -> String { - match Hive::LocalMachine.open(format!("SOFTWARE\\Wow6432Node\\EA Games\\{}", cfg.game.full_name()), Security::Read) { - Ok(regkey) => { - match regkey.value("Install Dir") { - Ok(result) => format!("{}\\{}", result, cfg.game.process_start()), - Err(_) => { - log::warn!("{} not found in ea desktop's registry, using default origin location.", cfg.game.full_name()); - format!("C:\\Program Files (x86)\\Origin Games\\{}\\{}", cfg.game.full_name(), cfg.game.process_start()) - }, + match Hive::LocalMachine.open( + format!("SOFTWARE\\Wow6432Node\\EA Games\\{}", cfg.game.full_name()), + Security::Read, + ) { + Ok(regkey) => match regkey.value("Install Dir") { + Ok(result) => format!("{}\\{}", result, cfg.game.process_start()), + Err(_) => { + log::warn!( + "{} not found in ea desktop's registry, using default origin location.", + cfg.game.full_name() + ); + format!( + "C:\\Program Files (x86)\\Origin Games\\{}\\{}", + cfg.game.full_name(), + cfg.game.process_start() + ) } }, Err(_) => { - log::warn!("{} not found in ea desktop's registry, using default origin location.", cfg.game.full_name()); - format!("C:\\Program Files (x86)\\Origin Games\\{}\\{}", cfg.game.full_name(), cfg.game.process_start()) + log::warn!( + "{} not found in ea desktop's registry, using default origin location.", + cfg.game.full_name() + ); + format!( + "C:\\Program Files (x86)\\Origin Games\\{}\\{}", + cfg.game.full_name(), + cfg.game.process_start() + ) } } } @@ -98,7 +117,9 @@ pub fn send_message(to_send: String, cfg: &structs::SeederConfig) { sleep(Duration::from_millis(2000)); let mut message: Vec = Vec::new(); for char in to_send.chars() { - if let Some(dx) = char_to_dxcodes(char) { message.push(dx) } + if let Some(dx) = char_to_dxcodes(char) { + message.push(dx) + } } send_keys::send_string(message); sleep(Duration::from_millis(100)); @@ -124,7 +145,11 @@ pub fn is_running(cfg: &structs::SeederConfig) -> structs::GameInfo { } } -pub fn quit(cfg: &structs::SeederConfig, game_running: &Arc, retry_launch: &Arc) { +pub fn quit( + cfg: &structs::SeederConfig, + game_running: &Arc, + retry_launch: &Arc, +) { log::info!("Quitting old session.."); let game_process = winproc::Process::from_name(cfg.game.process_name()); match game_process { @@ -156,11 +181,16 @@ pub fn quit(cfg: &structs::SeederConfig, game_running: &Arc, retry_la } } -pub fn launch(cfg: &structs::SeederConfig, game_id: &str, role: &str, - game_running: &Arc, retry_launch: &Arc) { +pub fn launch( + cfg: &structs::SeederConfig, + game_id: &str, + role: &str, + game_running: &Arc, + retry_launch: &Arc, +) { if game_running.load(atomic::Ordering::Relaxed) == 1 { // if it tried to launch but failed twice - if retry_launch.load(atomic::Ordering::Relaxed) == 10 { + if retry_launch.load(atomic::Ordering::Relaxed) >= 10 { launchers::restart_launcher(cfg); // make retries 0 retry_launch.store(0, atomic::Ordering::Relaxed); diff --git a/src/actions/launchers.rs b/src/actions/launchers.rs index 0d67a29..7e69513 100644 --- a/src/actions/launchers.rs +++ b/src/actions/launchers.rs @@ -1,17 +1,17 @@ +use crate::structs; +use directories::BaseDirs; +use ini::Ini; +use regex::Regex; use std::ffi::OsStr; +use std::fs; use std::iter::once; use std::os::windows::prelude::OsStrExt; use std::process::Command; use std::ptr; use std::thread::sleep; use std::time::{Duration, UNIX_EPOCH}; -use std::fs; use winapi::shared::windef::HWND__; use winapi::um::winuser::FindWindowW; -use regex::Regex; -use ini::Ini; -use directories::BaseDirs; -use crate::structs; pub fn launch_game(cfg: &structs::SeederConfig, game_id: &str, role: &str) { if cfg.use_ea_desktop { @@ -57,9 +57,9 @@ pub fn launch_game_ea_desktop(cfg: &structs::SeederConfig, game_id: &str, role: let mut timeout = 0; let mut not_running = true; - while not_running - { - if timeout > 10 { // give up on to many tries waiting and continue anyway + while not_running { + if timeout > 10 { + // give up on to many tries waiting and continue anyway log::warn!("waiting to long, continueing.."); break; } @@ -91,7 +91,7 @@ pub fn launch_game_origin(cfg: &structs::SeederConfig, game_id: &str, role: &str "-joinWithParty", "false", ]); - }, + } structs::Games::Bf1 => { if cfg.usable_client { command.args([ @@ -150,7 +150,7 @@ pub fn launch_game_origin(cfg: &structs::SeederConfig, game_id: &str, role: &str &(role == "spectator").to_string()[..], ]); } - }, + } }; match command.spawn() { Ok(_) => log::info!("game launched"), @@ -167,10 +167,7 @@ pub fn is_launcher_running(cfg: &structs::SeederConfig) -> structs::GameInfo { pub fn is_origin_running() -> structs::GameInfo { unsafe { - let window: Vec = OsStr::new("Origin") - .encode_wide() - .chain(once(0)) - .collect(); + let window: Vec = OsStr::new("Origin").encode_wide().chain(once(0)).collect(); let window_handle = FindWindowW(std::ptr::null_mut(), window.as_ptr()); let no_game: *mut HWND__ = ptr::null_mut(); structs::GameInfo { @@ -182,10 +179,7 @@ pub fn is_origin_running() -> structs::GameInfo { pub fn is_ea_desktop_running() -> structs::GameInfo { unsafe { - let window: Vec = OsStr::new("EA") - .encode_wide() - .chain(once(0)) - .collect(); + let window: Vec = OsStr::new("EA").encode_wide().chain(once(0)).collect(); let window_handle = FindWindowW(std::ptr::null_mut(), window.as_ptr()); let no_game: *mut HWND__ = ptr::null_mut(); structs::GameInfo { @@ -215,7 +209,7 @@ pub fn stop_ea_desktop() { log::info!("Closed EA Desktop"); sleep(Duration::from_secs(10)); } - Err(e) => log::error!("failed to close EA Desktop (likely permissions): {}", e) + Err(e) => log::error!("failed to close EA Desktop (likely permissions): {}", e), }, Err(_) => { log::info!("EA desktop not found!"); @@ -233,18 +227,17 @@ pub fn restart_origin() { log::info!("Closed Origin"); sleep(Duration::from_secs(10)); } - Err(e) => log::error!("failed to close origin (likely permissions): {}", e) + Err(e) => log::error!("failed to close origin (likely permissions): {}", e), }, Err(_) => { log::info!("origin not found!"); } } - match command.spawn() - { + match command.spawn() { Ok(_) => { log::info!("origin launched"); sleep(Duration::from_secs(20)); - }, + } Err(e) => log::error!("failed to launch origin: {}", e), } } @@ -268,7 +261,11 @@ pub fn edit_ea_desktop(cfg: &structs::SeederConfig, launch_settings: String) { Err(_) => return log::error!("EA Desktop folder not found in AppData!"), }; - let mut newest_file = structs::EaDesktopNewestFile { time: 0, location: "".into(), file_name: "".into() }; + let mut newest_file = structs::EaDesktopNewestFile { + time: 0, + location: "".into(), + file_name: "".into(), + }; let re = match Regex::new(r"^user_.*.ini$") { Ok(re) => re, Err(_) => return log::error!("Invalid REGEX for gathering EA desktop"), @@ -277,7 +274,6 @@ pub fn edit_ea_desktop(cfg: &structs::SeederConfig, launch_settings: String) { // check filename errors match path.file_name().to_str() { Some(name) => { - // get modified time in secs match path.metadata() { Ok(e) => match e.modified() { @@ -287,7 +283,6 @@ pub fn edit_ea_desktop(cfg: &structs::SeederConfig, launch_settings: String) { // check if newer and use only .ini files if re.is_match(name) && timestamp > newest_file.time { - // set to newest if true match path.path().to_str() { Some(location) => { @@ -296,7 +291,7 @@ pub fn edit_ea_desktop(cfg: &structs::SeederConfig, launch_settings: String) { time: timestamp.to_owned(), location: location.to_owned(), } - }, + } None => continue, }; } @@ -307,7 +302,7 @@ pub fn edit_ea_desktop(cfg: &structs::SeederConfig, launch_settings: String) { }, Err(_) => continue, }; - }, + } None => continue, }; } @@ -329,8 +324,10 @@ pub fn edit_ea_desktop(cfg: &structs::SeederConfig, launch_settings: String) { Some(section) => section, None => return log::error!("Empty EA Desktop config file!"), }; - new_conf.with_section(None::).set("user.gamecommandline.origin.ofr.50.0002683", ""); - + new_conf + .with_section(None::) + .set("user.gamecommandline.origin.ofr.50.0002683", ""); + let game_versions = cfg.game.game_versions(); // copy old config for (key, value) in old_section.iter() { @@ -342,11 +339,11 @@ pub fn edit_ea_desktop(cfg: &structs::SeederConfig, launch_settings: String) { } else { conf.insert(key, value) } - }, + } None => log::error!("Failed to copy {:?}:{:?}", key, value), }; } - + if let Some(conf) = new_conf.section_mut(None::) { for game_version in game_versions { conf.insert(game_version, launch_settings.clone()); @@ -354,7 +351,7 @@ pub fn edit_ea_desktop(cfg: &structs::SeederConfig, launch_settings: String) { } match new_conf.write_to_file(newest_file.location) { - Ok(_) => {}, + Ok(_) => {} Err(e) => log::error!("Failed to save new EA Desktop config: {}", e), }; } diff --git a/src/actions/mod.rs b/src/actions/mod.rs index 96934c8..32622c2 100644 --- a/src/actions/mod.rs +++ b/src/actions/mod.rs @@ -1,3 +1,3 @@ pub mod backend; pub mod game; -pub mod launchers; \ No newline at end of file +pub mod launchers; diff --git a/src/ea_desktop_test.rs b/src/ea_desktop_test.rs index 43522c2..f7f9d1a 100644 --- a/src/ea_desktop_test.rs +++ b/src/ea_desktop_test.rs @@ -1,7 +1,7 @@ -use std::io::Write; use chrono::Local; use env_logger::Builder; use log::LevelFilter; +use std::io::Write; mod actions; mod functions; mod input; @@ -9,16 +9,17 @@ mod structs; fn main() { Builder::new() - .format(|buf, record| { - writeln!(buf, - "{} [{}] - {}", - Local::now().format("%Y-%m-%dT%H:%M:%S"), - record.level(), - record.args() - ) - }) - .filter(None, LevelFilter::Info) - .init(); + .format(|buf, record| { + writeln!( + buf, + "{} [{}] - {}", + Local::now().format("%Y-%m-%dT%H:%M:%S"), + record.level(), + record.args() + ) + }) + .filter(None, LevelFilter::Info) + .init(); let mut cfg = structs::SeederConfig { hostname: hostname::get().unwrap().into_string().unwrap(), @@ -35,8 +36,10 @@ fn main() { message_stop_time_utc: "23:00".into(), message_timeout_mins: 8, game: structs::Games::from("bf1"), + seeder_name: "".into(), + find_player_max_retries: 15, }; cfg.game_location = actions::game::find_game(&cfg); actions::launchers::launch_game_ea_desktop(&cfg, "7821536030132", "soldier"); -} \ No newline at end of file +} diff --git a/src/functions/anti_afk.rs b/src/functions/anti_afk.rs index e407861..6f60edb 100644 --- a/src/functions/anti_afk.rs +++ b/src/functions/anti_afk.rs @@ -54,4 +54,4 @@ pub fn start( } } sleep(Duration::from_secs(120)); -} \ No newline at end of file +} diff --git a/src/functions/auto_message.rs b/src/functions/auto_message.rs index d3914ca..d25435f 100644 --- a/src/functions/auto_message.rs +++ b/src/functions/auto_message.rs @@ -58,10 +58,19 @@ pub fn start( "https://api.gametools.network/{}/servers/?name={}®ion=all&platform=pc&limit=1&lang=en-us", cfg.game.short_name() ,encode(&cfg.message_server_name[..]) ); - match ureq::get(&connect_addr[..]).timeout(Duration::new(10, 0)).call() { + match ureq::get(&connect_addr[..]) + .timeout(Duration::new(10, 0)) + .call() + { Ok(response) => match response.into_json::() { Ok(server_info) => { - actions::game::launch(cfg, &server_info.servers[0].game_id, "spectator", game_running, retry_launch); + actions::game::launch( + cfg, + &server_info.servers[0].game_id, + "spectator", + game_running, + retry_launch, + ); } Err(_) => log::error!("Servername not found"), }, @@ -75,4 +84,4 @@ pub fn start( message_running.store(0, atomic::Ordering::Relaxed); } } -} \ No newline at end of file +} diff --git a/src/functions/mod.rs b/src/functions/mod.rs index 2555b31..c384878 100644 --- a/src/functions/mod.rs +++ b/src/functions/mod.rs @@ -1,3 +1,3 @@ pub mod anti_afk; pub mod auto_message; -pub mod seed_server; \ No newline at end of file +pub mod seed_server; diff --git a/src/functions/seed_server.rs b/src/functions/seed_server.rs index 885598b..3c44c9a 100644 --- a/src/functions/seed_server.rs +++ b/src/functions/seed_server.rs @@ -1,12 +1,12 @@ use std::sync::atomic::AtomicU32; use std::sync::{atomic, Arc}; -use system_shutdown::shutdown; use system_shutdown::reboot; +use system_shutdown::shutdown; use crate::actions; use crate::structs; -fn multialive ( +fn multialive( cfg: &structs::SeederConfig, game_info: &structs::GameInfo, old_seeder_info: &structs::CurrentServer, @@ -17,22 +17,21 @@ fn multialive ( message_running: &Arc, ) { //if gameid is different then old game id, or seedername not present in old arr, leave current session and start new - if game_info.is_running - && (old_game_id != current_game_id && &old_seeder_info.action[..] != "leaveServer") - || (message_running.load(atomic::Ordering::Relaxed) == 1) + if game_info.is_running + && (old_game_id != current_game_id && &old_seeder_info.action[..] != "leaveServer") + || (message_running.load(atomic::Ordering::Relaxed) == 1) { actions::game::quit(cfg, game_running, retry_launch); // message is not running while seeding message_running.store(0, atomic::Ordering::Relaxed); } - if !game_info.is_running - { + if !game_info.is_running { actions::game::launch(cfg, current_game_id, "soldier", game_running, retry_launch); } game_running.store(1, atomic::Ordering::Relaxed); } -fn on_command_changed ( +fn on_command_changed( cfg: &structs::SeederConfig, kp_seeder: bool, game_info: &structs::GameInfo, @@ -47,11 +46,20 @@ fn on_command_changed ( let a_minute = seeder_info.timestamp < chrono::Utc::now().timestamp() - 60; // 1 minute since last request if kp_seeder { - multialive(cfg, game_info, old_seeder_info, current_game_id, old_game_id, game_running, retry_launch, message_running); + multialive( + cfg, + game_info, + old_seeder_info, + current_game_id, + old_game_id, + game_running, + retry_launch, + message_running, + ); } else if &seeder_info.action[..] == "joinServer" { // remove old session when switching to fast if (old_game_id != current_game_id && &old_seeder_info.action[..] != "leaveServer") - || (message_running.load(atomic::Ordering::Relaxed) == 1) + || (message_running.load(atomic::Ordering::Relaxed) == 1) { actions::game::quit(cfg, game_running, retry_launch); // message is not running while seeding @@ -64,8 +72,7 @@ fn on_command_changed ( // game state == running game game_running.store(1, atomic::Ordering::Relaxed); } else if &seeder_info.action[..] == "restartOrigin" && !a_minute { - if game_info.is_running - { + if game_info.is_running { actions::game::quit(cfg, game_running, retry_launch); } actions::launchers::restart_launcher(cfg); @@ -88,36 +95,59 @@ fn on_command_changed ( } } -fn on_retry ( +fn retry_check( cfg: &structs::SeederConfig, kp_seeder: bool, game_info: &structs::GameInfo, seeder_info: &structs::CurrentServer, current_game_id: &str, game_running: &Arc, - retry_launch: &Arc + retry_launch: &Arc, + retry_player_check: &Arc, ) { + // if game isnt running but should: retry if !&game_info.is_running - && ((&seeder_info.action[..] == "joinServer" && seeder_info.rejoin) - || kp_seeder) + && ((&seeder_info.action[..] == "joinServer" && seeder_info.rejoin) || kp_seeder) { log::warn!("didn't find game running, starting.."); actions::game::launch(cfg, current_game_id, "soldier", game_running, retry_launch); } - //set retries 0 - if game_info.is_running - { + // if game is running, check if in right server if option set + if game_info.is_running { retry_launch.store(0, atomic::Ordering::Relaxed); + + // check if player is in server + if !cfg.seeder_name.is_empty() { + let retries = retry_player_check.load(atomic::Ordering::Relaxed); + if actions::backend::has_player(cfg, current_game_id) { + if retries > 0 { + log::info!("player found"); + } + retry_player_check.store(0, atomic::Ordering::Relaxed) + } else if retries >= cfg.find_player_max_retries { + log::error!( + "player is still not in the server after {} retries", + retries + ); + actions::game::quit(cfg, game_running, retry_launch); + actions::game::launch(cfg, current_game_id, "soldier", game_running, retry_launch); + retry_player_check.store(0, atomic::Ordering::Relaxed); + } else { + retry_player_check.fetch_add(1, atomic::Ordering::Relaxed); + log::info!("player not yet found, try number: {}", retries); + } + } } } -pub fn start ( +pub fn start( seeder_info: structs::CurrentServer, old_seeder_info: &mut structs::CurrentServer, cfg: &structs::SeederConfig, game_running: &Arc, retry_launch: &Arc, message_running: &Arc, + retry_player_check: &Arc, ) { let game_info = actions::game::is_running(cfg); let a_hour = seeder_info.timestamp < chrono::Utc::now().timestamp() - 3600; // 1 hour since last request @@ -126,25 +156,46 @@ pub fn start ( let mut old_game_id = &old_seeder_info.game_id[..]; // previous check (if changed, will be used to switch server) // set keepalive info when being used in multilalive - if seeder_info.keep_alive_seeders.contains_key(&cfg.hostname) - { - kp_seeder = true; + if seeder_info.keep_alive_seeders.contains_key(&cfg.hostname) { + kp_seeder = true; current_game_id = &seeder_info.keep_alive_seeders[&cfg.hostname]["gameId"]; } - if old_seeder_info.keep_alive_seeders.contains_key(&cfg.hostname) + if old_seeder_info + .keep_alive_seeders + .contains_key(&cfg.hostname) { old_game_id = &old_seeder_info.keep_alive_seeders[&cfg.hostname]["gameId"]; } // Actions when the seeding command changed if seeder_info.timestamp != old_seeder_info.timestamp && !a_hour { - on_command_changed(cfg, kp_seeder, &game_info, &seeder_info, old_seeder_info, current_game_id, old_game_id, game_running, retry_launch, message_running); + on_command_changed( + cfg, + kp_seeder, + &game_info, + &seeder_info, + old_seeder_info, + current_game_id, + old_game_id, + game_running, + retry_launch, + message_running, + ); // request to old to work with } else if seeder_info.timestamp != old_seeder_info.timestamp && a_hour { log::info!("request older than a hour, not running latest request.") - // retry if game isnt running + // if no new action } else { - on_retry(cfg, kp_seeder, &game_info, &seeder_info, current_game_id, game_running, retry_launch); + retry_check( + cfg, + kp_seeder, + &game_info, + &seeder_info, + current_game_id, + game_running, + retry_launch, + retry_player_check, + ); } // ping backend diff --git a/src/input/chars.rs b/src/input/chars.rs index 0126d89..06f281d 100644 --- a/src/input/chars.rs +++ b/src/input/chars.rs @@ -13,13 +13,13 @@ const CHAR_MAPPING: [u16; 47] = [ 0x08, //7 0x09, //8 0x0A, //9 - 0x0, + 0x0, //? 0x27, //; - 0x0, + 0x0, //? 0x0D, //= - 0x0, - 0x0, - 0x0, + 0x0, //? + 0x0, //? + 0x0, //? 0x1E, //A 0x30, //B 0x2E, //C @@ -46,24 +46,24 @@ const CHAR_MAPPING: [u16; 47] = [ 0x2D, //X 0x15, //Y 0x2C, //Z -]; +]; #[derive(Debug)] pub enum DXCode { Symbol(u16), - Shifted(u16) + Shifted(u16), } /** - Convert ASCII char into DirectX key code - */ + Convert ASCII char into DirectX key code +*/ pub fn char_to_dxcodes(c: char) -> Option { let mut c_u8 = c as u8; if c.is_ascii_lowercase() { c_u8 &= 0xdf; } - + if c.is_ascii_whitespace() { return Some(DXCode::Symbol(0x39)); } @@ -86,4 +86,4 @@ pub fn char_to_dxcodes(c: char) -> Option { } else { None } -} \ No newline at end of file +} diff --git a/src/input/mod.rs b/src/input/mod.rs index 3a265c7..34f49b6 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1,2 +1,2 @@ +pub mod chars; pub mod send_keys; -pub mod chars; \ No newline at end of file diff --git a/src/input/send_keys.rs b/src/input/send_keys.rs index 4654d67..e853db2 100644 --- a/src/input/send_keys.rs +++ b/src/input/send_keys.rs @@ -1,5 +1,7 @@ use std::{mem, thread::sleep, time::Duration}; -use winapi::um::winuser::{INPUT, INPUT_KEYBOARD, KEYEVENTF_EXTENDEDKEY, KEYEVENTF_KEYUP, KEYEVENTF_SCANCODE, SendInput}; +use winapi::um::winuser::{ + SendInput, INPUT, INPUT_KEYBOARD, KEYEVENTF_EXTENDEDKEY, KEYEVENTF_KEYUP, KEYEVENTF_SCANCODE, +}; use crate::input::chars::DXCode; // key codes: https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes @@ -32,7 +34,7 @@ unsafe fn special_down(key_code: u16) { } unsafe fn special_up(key_code: u16) { - let mut input = create_input(0, key_code, KEYEVENTF_EXTENDEDKEY|KEYEVENTF_KEYUP); + let mut input = create_input(0, key_code, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP); SendInput(1, &mut input, mem::size_of::() as i32); } @@ -45,7 +47,7 @@ pub unsafe fn key_enter(key_code: u16, timeout: u64) { pub unsafe fn send_string(keys: Vec) { for key in keys { match key { - DXCode::Shifted(code)=>{ + DXCode::Shifted(code) => { sleep(Duration::from_millis(10)); special_down(0x10); sleep(Duration::from_millis(10)); @@ -53,8 +55,8 @@ pub unsafe fn send_string(keys: Vec) { sleep(Duration::from_millis(10)); special_up(0x10); sleep(Duration::from_millis(10)); - }, + } DXCode::Symbol(code) => key_enter(code, 8), } } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index a50c4b1..5cb5bbf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,31 +1,31 @@ +use chrono::Local; +use env_logger::Builder; +use log::LevelFilter; +use std::collections::HashMap; +use std::io::Write; use std::{ sync::{atomic, Arc}, thread::{self, sleep}, time::Duration, }; -use std::io::Write; -use chrono::Local; -use env_logger::Builder; -use log::LevelFilter; -use std::collections::HashMap; mod actions; mod functions; mod input; mod structs; - fn main() { Builder::new() - .format(|buf, record| { - writeln!(buf, - "{} [{}] - {}", - Local::now().format("%Y-%m-%dT%H:%M:%S"), - record.level(), - record.args() - ) - }) - .filter(None, LevelFilter::Info) - .init(); + .format(|buf, record| { + writeln!( + buf, + "{} [{}] - {}", + Local::now().format("%Y-%m-%dT%H:%M:%S"), + record.level(), + record.args() + ) + }) + .filter(None, LevelFilter::Info) + .init(); // game_running based on api, 0 == leaving servers. 1 means joining servers. let game_running = Arc::new(atomic::AtomicU32::new(0)); @@ -42,6 +42,7 @@ fn main() { let retry_launch = Arc::new(atomic::AtomicU32::new(0)); let retry_launch_clone_message = Arc::clone(&retry_launch); + let retry_player_check = Arc::new(atomic::AtomicU32::new(0)); // get/set config let cfg: structs::SeederConfig = match confy::load_path("config.txt") { Ok(config) => config, @@ -63,12 +64,14 @@ fn main() { message_stop_time_utc: "23:00".into(), message_timeout_mins: 8, game: structs::Games::from("bf1"), + seeder_name: "".into(), + find_player_max_retries: 15, }; cfg.game_location = actions::game::find_game(&cfg); cfg } }; - + confy::store_path("config.txt", cfg.clone()).unwrap(); if cfg.group_id.is_empty() { log::warn!("group_id isn't set!"); @@ -82,7 +85,7 @@ fn main() { &game_running_clone_anti_afk, &message_running_clone_anti_afk, &message_timeout, - ¤t_message_id + ¤t_message_id, ) }); @@ -114,7 +117,10 @@ fn main() { ); log::info!("firing of latest request found (default on startup script)"); loop { - match ureq::get(&connect_addr[..]).timeout(Duration::new(10, 0)).call() { + match ureq::get(&connect_addr[..]) + .timeout(Duration::new(10, 0)) + .call() + { Ok(response) => match response.into_json::() { Ok(seeder_info) => { functions::seed_server::start( @@ -124,6 +130,7 @@ fn main() { &game_running, &retry_launch, &message_running_clone, + &retry_player_check, ); } Err(e) => { diff --git a/src/origin_test.rs b/src/origin_test.rs index c503fa7..3ef6152 100644 --- a/src/origin_test.rs +++ b/src/origin_test.rs @@ -1,7 +1,7 @@ -use std::io::Write; use chrono::Local; use env_logger::Builder; use log::LevelFilter; +use std::io::Write; mod actions; mod functions; mod input; @@ -9,16 +9,17 @@ mod structs; fn main() { Builder::new() - .format(|buf, record| { - writeln!(buf, - "{} [{}] - {}", - Local::now().format("%Y-%m-%dT%H:%M:%S"), - record.level(), - record.args() - ) - }) - .filter(None, LevelFilter::Info) - .init(); + .format(|buf, record| { + writeln!( + buf, + "{} [{}] - {}", + Local::now().format("%Y-%m-%dT%H:%M:%S"), + record.level(), + record.args() + ) + }) + .filter(None, LevelFilter::Info) + .init(); let mut cfg = structs::SeederConfig { hostname: hostname::get().unwrap().into_string().unwrap(), @@ -35,8 +36,10 @@ fn main() { message_stop_time_utc: "23:00".into(), message_timeout_mins: 8, game: structs::Games::from("bf1"), + seeder_name: "".into(), + find_player_max_retries: 15, }; cfg.game_location = actions::game::find_game(&cfg); actions::launchers::launch_game_origin(&cfg, "7821536030132", "soldier"); -} \ No newline at end of file +} diff --git a/src/structs.rs b/src/structs.rs index 752ceb3..10396f7 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,7 +1,7 @@ +use crate::actions; use serde_derive::{Deserialize, Serialize}; -use winapi::shared::windef::HWND__; use std::collections::HashMap; -use crate::actions; +use winapi::shared::windef::HWND__; #[derive(Serialize, Deserialize, Debug)] pub struct BroadcastMessage { @@ -26,6 +26,8 @@ pub struct SeederConfig { pub message_stop_time_utc: String, pub message_timeout_mins: u32, pub game: Games, + pub seeder_name: String, + pub find_player_max_retries: u32, } #[derive(Deserialize, PartialEq, Eq, Clone, Debug)] @@ -68,6 +70,8 @@ impl ::std::default::Default for SeederConfig { message_stop_time_utc: "23:00".into(), message_timeout_mins: 8, game: Games::from("bf1"), + seeder_name: "".into(), + find_player_max_retries: 15, }; cfg.game_location = actions::game::find_game(&cfg); cfg @@ -85,17 +89,40 @@ pub struct ServerInfo { pub game_id: String, } +#[derive(Deserialize, PartialEq, Eq, Clone, Debug)] +pub struct GametoolsServerPlayer { + pub name: String, +} + +#[derive(Deserialize, PartialEq, Eq, Clone, Debug)] +pub struct GametoolsDetailedServer { + #[serde(rename = "gameId")] + pub game_id: String, + pub players: Option>, +} + +#[derive(Deserialize, PartialEq, Eq, Clone, Debug)] +pub struct GametoolsTeam { + pub players: Vec, +} + +#[derive(Deserialize, PartialEq, Eq, Clone, Debug)] +pub struct GametoolsPlayers { + pub teams: Vec, + pub update_timestamp: i64, +} + #[derive(Clone, Debug)] pub struct EaDesktopNewestFile { pub file_name: String, pub time: u64, - pub location: String + pub location: String, } -#[derive(Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)] pub enum Games { Bf4, - Bf1 + Bf1, } impl Games { @@ -110,35 +137,35 @@ impl Games { pub fn full_name(&self) -> &'static str { match self { Games::Bf4 => "Battlefield 4", - Games::Bf1 => "Battlefield 1" + Games::Bf1 => "Battlefield 1", } } pub fn window_name(&self) -> &'static str { match self { Games::Bf4 => "Battlefield 4", - Games::Bf1 => "Battlefield™ 1" + Games::Bf1 => "Battlefield™ 1", } } pub fn process_name(&self) -> &'static str { match self { Games::Bf4 => "bf4.exe", - Games::Bf1 => "bf1.exe" + Games::Bf1 => "bf1.exe", } } pub fn process_start(&self) -> &'static str { match self { Games::Bf4 => "BFLauncher_x86.exe", - Games::Bf1 => "bf1.exe" + Games::Bf1 => "bf1.exe", } } pub fn short_name(&self) -> &'static str { match self { Games::Bf4 => "bf4", - Games::Bf1 => "bf1" + Games::Bf1 => "bf1", } } @@ -148,14 +175,14 @@ impl Games { "user.gamecommandline.origin.ofr.50.0002683", "user.gamecommandline.ofb-east:109552316", "user.gamecommandline.ofb-east:109546867", - "user.gamecommandline.ofb-east:109549060" + "user.gamecommandline.ofb-east:109549060", ], Games::Bf1 => vec![ "user.gamecommandline.origin.ofr.50.0000557", "user.gamecommandline.origin.ofr.50.0001382", "user.gamecommandline.origin.ofr.50.0001665", - "user.gamecommandline.origin.ofr.50.0001662" - ] + "user.gamecommandline.origin.ofr.50.0001662", + ], } } -} \ No newline at end of file +} From 03eea6397298a39ca6930482d31a8ef5be4646d5 Mon Sep 17 00:00:00 2001 From: Zefanja Jobse Date: Sat, 14 Jan 2023 22:35:40 +0100 Subject: [PATCH 2/2] fix numbering --- src/functions/seed_server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/functions/seed_server.rs b/src/functions/seed_server.rs index 3c44c9a..7e9c324 100644 --- a/src/functions/seed_server.rs +++ b/src/functions/seed_server.rs @@ -134,7 +134,7 @@ fn retry_check( retry_player_check.store(0, atomic::Ordering::Relaxed); } else { retry_player_check.fetch_add(1, atomic::Ordering::Relaxed); - log::info!("player not yet found, try number: {}", retries); + log::info!("player not yet found, try number: {}", retries + 1); } } }