From 74132ec322624fb93b6688ea89bd0c641ad31f4e Mon Sep 17 00:00:00 2001 From: futile Date: Fri, 21 Nov 2025 11:09:18 +0000 Subject: [PATCH] heavily modify game_networking & fully implement save server blocking --- src/features/game_networking.rs | 143 ++++++++++++++++++++++++-------- src/gui/app.rs | 97 +++++++++++++--------- 2 files changed, 167 insertions(+), 73 deletions(-) diff --git a/src/features/game_networking.rs b/src/features/game_networking.rs index afad2a8..1a9c4b3 100644 --- a/src/features/game_networking.rs +++ b/src/features/game_networking.rs @@ -1,9 +1,13 @@ -use crate::util::{ - consts::game::{EXE_ENHANCED, EXE_LEGACY}, - system_info::SystemInfo, +use crate::{ + gui::settings::BlockMethod, + util::{ + consts::game::{EXE_ENHANCED, EXE_LEGACY}, + system_info::SystemInfo, + }, }; use std::{ - path::Path, + error::Error, + path::{Path, PathBuf}, time::{Duration, Instant}, }; use strum::{Display, EnumIter}; @@ -22,6 +26,7 @@ use windows::{ }; const FILTER_NAME_EXE: &str = "[GTA Tools] Block outbound traffic for all of GTA V"; +const FILTER_NAME_SAVE_SERVER: &str = "[GTA Tools] Block outbound traffic to Rockstar save server"; const INTERVAL: Duration = Duration::from_secs(3); @@ -53,7 +58,11 @@ pub struct GameNetworking { impl Default for GameNetworking { fn default() -> Self { Self { - blocked_status: Self::is_blocked().into(), + blocked_status: if Self::is_save_server_blocked().unwrap() == true { + Self::is_save_server_blocked().unwrap().into() + } else { + Self::is_exe_blocked().unwrap().into() + }, com_initialized: unsafe { CoInitializeEx(None, COINIT_MULTITHREADED) }.is_ok(), timer: Instant::now(), counting: false, @@ -69,46 +78,89 @@ impl Drop for GameNetworking { } } +enum Mode { + EntireGame(PathBuf), + SaveServer(String), +} + impl GameNetworking { - pub fn block_exe(&mut self, system_info: &mut SystemInfo) { + fn block_generic(&mut self, mode: Mode) -> Result<(), Box> { + let policy: INetFwPolicy2 = + unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }?; + let rules = unsafe { policy.Rules() }?; + unsafe { rules.Remove(&BSTR::from(FILTER_NAME_SAVE_SERVER)) }?; + let rule: INetFwRule = unsafe { CoCreateInstance(&NetFwRule, None, CLSCTX_INPROC_SERVER) }?; + unsafe { rule.SetName(&BSTR::from(FILTER_NAME_SAVE_SERVER)) }?; + match mode { + Mode::EntireGame(exe_path) => { + let exe_path = BSTR::from(exe_path.to_string_lossy().to_string()); + unsafe { rule.SetApplicationName(&exe_path) }?; + } + Mode::SaveServer(save_server_ip) => { + unsafe { rule.SetRemoteAddresses(&BSTR::from(save_server_ip)) }?; + } + } + unsafe { rule.SetDirection(NET_FW_RULE_DIR_OUT) }?; + unsafe { rule.SetEnabled(true.into()) }?; + unsafe { rule.SetAction(NET_FW_ACTION_BLOCK) }?; + unsafe { rule.SetProtocol(NET_FW_IP_PROTOCOL_ANY.0) }?; + unsafe { rules.Add(&rule) }?; + Ok(()) + } + + fn unblock_generic(&mut self, filter_name: &str) -> Result<(), Box> { + let policy: INetFwPolicy2 = + unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }?; + let rules = unsafe { policy.Rules() }?; + unsafe { rules.Remove(&BSTR::from(filter_name)) }?; + Ok(()) + } + + fn is_blocked_generic(filter_name: &str) -> Result> { + let policy: INetFwPolicy2 = + unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }?; + let rules = unsafe { policy.Rules() }?; + let rule_exists = unsafe { rules.Item(&BSTR::from(filter_name)) }.is_ok(); + Ok(rule_exists) + } + + pub fn block_exe(&mut self, system_info: &mut SystemInfo) -> Result<(), Box> { let Some(exe_path) = get_game_exe_path(system_info) else { self.blocked_status = BlockedStatus::Failed; - return; + return Ok(()); }; - let policy: INetFwPolicy2 = - unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }.unwrap(); - let rules = unsafe { policy.Rules() }.unwrap(); - let exe_path = BSTR::from(exe_path.to_string_lossy().to_string()); - unsafe { rules.Remove(&BSTR::from(FILTER_NAME_EXE)) }.unwrap(); - let rule: INetFwRule = - unsafe { CoCreateInstance(&NetFwRule, None, CLSCTX_INPROC_SERVER) }.unwrap(); - unsafe { rule.SetName(&BSTR::from(FILTER_NAME_EXE)) }.unwrap(); - unsafe { rule.SetApplicationName(&exe_path) }.unwrap(); - unsafe { rule.SetDirection(NET_FW_RULE_DIR_OUT) }.unwrap(); - unsafe { rule.SetEnabled(true.into()) }.unwrap(); - unsafe { rule.SetAction(NET_FW_ACTION_BLOCK) }.unwrap(); - unsafe { rule.SetProtocol(NET_FW_IP_PROTOCOL_ANY.0) }.unwrap(); - unsafe { rules.Add(&rule) }.unwrap(); - self.blocked_status = Self::is_blocked().into(); + self.block_generic(Mode::EntireGame(exe_path.to_path_buf()))?; + self.blocked_status = Self::is_exe_blocked()?.into(); + Ok(()) } - pub fn unblock_exe(&mut self) { - let policy: INetFwPolicy2 = - unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }.unwrap(); - let rules = unsafe { policy.Rules() }.unwrap(); - unsafe { rules.Remove(&BSTR::from(FILTER_NAME_EXE)) }.unwrap(); - self.blocked_status = Self::is_blocked().into(); + pub fn unblock_exe(&mut self) -> Result<(), Box> { + self.unblock_generic(FILTER_NAME_EXE)?; + self.blocked_status = Self::is_exe_blocked()?.into(); + Ok(()) } - fn is_blocked() -> bool { - let policy: INetFwPolicy2 = - unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }.unwrap(); - let rules = unsafe { policy.Rules() }.unwrap(); - let exe_rule_exists = unsafe { rules.Item(&BSTR::from(FILTER_NAME_EXE)) }.is_ok(); - exe_rule_exists + fn is_exe_blocked() -> Result> { + Self::is_blocked_generic(FILTER_NAME_EXE) } - pub fn reset_if_failed(&mut self) { + pub fn block_save_server(&mut self, save_server_ip: &str) -> Result<(), Box> { + self.block_generic(Mode::SaveServer(save_server_ip.to_owned()))?; + self.blocked_status = Self::is_save_server_blocked()?.into(); + Ok(()) + } + + pub fn unblock_save_server(&mut self) -> Result<(), Box> { + self.unblock_generic(FILTER_NAME_SAVE_SERVER)?; + self.blocked_status = Self::is_save_server_blocked()?.into(); + Ok(()) + } + + pub fn is_save_server_blocked() -> Result> { + Self::is_blocked_generic(FILTER_NAME_SAVE_SERVER) + } + + pub fn reset_indicator_if_failed(&mut self) { if self.blocked_status == BlockedStatus::Failed && !self.counting { self.counting = true; self.timer = Instant::now(); @@ -118,7 +170,26 @@ impl GameNetworking { && self.timer.elapsed() >= INTERVAL { self.counting = false; - self.blocked_status = Self::is_blocked().into(); + self.blocked_status = Self::is_exe_blocked().unwrap().into(); + } + } + + pub fn ensure_not_both_blocked_simultaneously(&mut self, block_method: BlockMethod) { + match block_method { + BlockMethod::EntireGame => { + if Self::is_save_server_blocked().unwrap() { + // ignoring the return because if this is an error the user can just thug it out at that point + let _ = self.unblock_save_server(); + self.blocked_status = Self::is_exe_blocked().unwrap().into(); + } + } + BlockMethod::SaveServer => { + if Self::is_exe_blocked().unwrap() { + // ignoring the return because if this is an error the user can just thug it out at that point + let _ = self.unblock_exe(); + self.blocked_status = Self::is_save_server_blocked().unwrap().into(); + } + } } } } diff --git a/src/gui/app.rs b/src/gui/app.rs index 11e6a01..6d165c6 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -160,15 +160,15 @@ impl App { egui::Frame::new() .outer_margin(egui::vec2(0.0, -2.0)) .show(ui, |ui| { - let response = ui.add_enabled_ui(self.flags.elevated, |ui| { + ui.add_enabled_ui(self.flags.elevated, |ui| { let label = ui.horizontal(|ui| { - let label = ui.label("Game's network access"); + let label = match self.settings.block_method { + BlockMethod::EntireGame => ui.label("Game's network access"), + BlockMethod::SaveServer => ui.label("Rockstar save server access"), + }; ui.add_space(1.0); - ui.create_indicator_dot(self.game_networking.blocked_status) - .on_hover_text( - "This turns yellow if GTA Tools\ncannot find your game.", - ); - self.game_networking.reset_if_failed(); + ui.create_indicator_dot(self.game_networking.blocked_status); + self.game_networking.reset_indicator_if_failed(); label }); ui.horizontal(|ui| { @@ -179,19 +179,36 @@ impl App { .add_sized([button_width, 18.0], egui::Button::new("Block")) .clicked() { - self.game_networking.block_exe(&mut self.system_info); + match self.settings.block_method { + BlockMethod::EntireGame => { + self.game_networking + .block_exe(&mut self.system_info) + .unwrap(); + } + BlockMethod::SaveServer => { + self.game_networking + .block_save_server(&self.settings.save_server_ip) + .unwrap(); + } + } } if ui .add_sized([button_width, 18.0], egui::Button::new("Unblock")) .clicked() { - self.game_networking.unblock_exe(); + match self.settings.block_method { + BlockMethod::EntireGame => { + self.game_networking.unblock_exe().unwrap(); + } + BlockMethod::SaveServer => { + self.game_networking.unblock_save_server().unwrap(); + } + } } }); - }); - response.response.on_disabled_hover_text( - "This requires administrator.\nUse the Elevate button.", - ); + }) + .response + .on_disabled_hover_text("This requires administrator.\nUse the Elevate button."); }); } @@ -228,30 +245,36 @@ impl App { }); }); ui.collapsing("Network", |ui| { - ui.horizontal(|ui| { - egui::ComboBox::from_id_salt("Block method") - .selected_text(self.settings.block_method.to_string()) - .show_ui(ui, |ui| { - ui.build_menu(&mut self.settings.block_method); - }); - ui.label("Block method"); - }); - ui.horizontal(|ui| { - ui.add_enabled_ui( - self.settings.block_method == BlockMethod::SaveServer, - |ui| { - ui.add( - egui::TextEdit::singleline(&mut self.settings.save_server_ip) - .char_limit(15) - .desired_width(92.0), - ); - ui.label("Save server IP"); - if ui.button("↺").clicked() { - self.settings.save_server_ip = String::from(ROCKSTAR_SAVE_SERVER); - }; - }, - ); - }); + ui.add_enabled_ui(self.flags.elevated, |ui| { + ui.horizontal(|ui| { + egui::ComboBox::from_id_salt("Block method") + .selected_text(self.settings.block_method.to_string()) + .show_ui(ui, |ui| { + ui.build_menu(&mut self.settings.block_method); + }); + ui.label("Block method"); + self.game_networking + .ensure_not_both_blocked_simultaneously(self.settings.block_method); + }); + ui.horizontal(|ui| { + ui.add_enabled_ui( + self.settings.block_method == BlockMethod::SaveServer, + |ui| { + ui.add( + egui::TextEdit::singleline(&mut self.settings.save_server_ip) + .char_limit(15) + .desired_width(92.0), + ); + ui.label("Save server IP"); + if ui.button("↺").clicked() { + self.settings.save_server_ip = String::from(ROCKSTAR_SAVE_SERVER); + }; + }, + ); + }); + }) + .response + .on_disabled_hover_text("This requires administrator.\nUse the Elevate button."); }); }