From ccc27b19449e1cbb47451a1d6319b8e6501e9a2b Mon Sep 17 00:00:00 2001 From: futile Date: Sun, 20 Apr 2025 01:25:25 +0100 Subject: [PATCH] slightly tweak `features::game_networking` design & add visual indicator for blocking failing due to game being closed --- src/features/game_networking.rs | 92 +++++++++++++++++++++++++++------ src/gui/app.rs | 7 +-- src/util/consts.rs | 7 +++ 3 files changed, 86 insertions(+), 20 deletions(-) diff --git a/src/features/game_networking.rs b/src/features/game_networking.rs index 76d5369..79978c8 100644 --- a/src/features/game_networking.rs +++ b/src/features/game_networking.rs @@ -1,5 +1,11 @@ -use crate::util::consts::game::{EXE_ENHANCED, EXE_LEGACY}; -use std::path::Path; +use crate::util::consts::{ + colours, + game::{EXE_ENHANCED, EXE_LEGACY}, +}; +use std::{ + path::Path, + time::{Duration, Instant}, +}; use sysinfo::System; use windows::{ Win32::{ @@ -13,28 +19,63 @@ use windows::{ CoUninitialize, }, }, - core::{BSTR, HRESULT}, + core::BSTR, }; const FILTER_NAME_IN: &str = "[GTA Tools] Block all inbound traffic for GTA V"; const FILTER_NAME_OUT: &str = "[GTA Tools] Block all outbound traffic for GTA V"; +const INTERVAL: Duration = Duration::from_secs(3); + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum BlockedStatus { + Blocked, + Failed, + Unblocked, +} + +impl From for BlockedStatus { + fn from(value: bool) -> Self { + match value { + true => Self::Blocked, + false => Self::Unblocked, + } + } +} + +impl From for eframe::egui::Color32 { + fn from(value: BlockedStatus) -> Self { + match value { + BlockedStatus::Blocked => colours::RED, + BlockedStatus::Failed => colours::YELLOW, + BlockedStatus::Unblocked => colours::GREEN, + } + } +} + +impl BlockedStatus { + pub fn to_color32(&self) -> eframe::egui::Color32 { + (*self).into() + } +} + #[derive(Debug)] pub struct GameNetworking { - pub is_blocked: bool, com_initialized: bool, - policy: INetFwPolicy2, + pub blocked_status: BlockedStatus, + timer: Instant, + counting: bool, } impl Default for GameNetworking { fn default() -> Self { - let result = unsafe { CoInitializeEx(None, COINIT_MULTITHREADED) }; let mut gn = Self { - is_blocked: false, - com_initialized: result != HRESULT(0x80010106u32 as i32), - policy: unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER).unwrap() }, + blocked_status: BlockedStatus::Unblocked, + com_initialized: unsafe { CoInitializeEx(None, COINIT_MULTITHREADED) }.is_ok(), + timer: Instant::now(), + counting: false, }; - gn.is_blocked = gn.is_blocked(); + gn.blocked_status = gn.is_blocked().into(); gn } } @@ -52,9 +93,12 @@ impl Drop for GameNetworking { impl GameNetworking { pub fn block_all(&mut self, sysinfo: &mut System) { let Some(exe_path) = get_game_exe_path(sysinfo) else { + self.blocked_status = BlockedStatus::Failed; return; }; - let rules = unsafe { self.policy.Rules().unwrap() }; + let policy: INetFwPolicy2 = + unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER).unwrap() }; + let rules = unsafe { policy.Rules().unwrap() }; let filter_name_in = BSTR::from(FILTER_NAME_IN); let filter_name_out = BSTR::from(FILTER_NAME_OUT); unsafe { @@ -84,11 +128,13 @@ impl GameNetworking { outbound_rule.SetProtocol(NET_FW_IP_PROTOCOL_ANY.0).unwrap(); rules.Add(&outbound_rule).unwrap(); } - self.is_blocked = self.is_blocked(); + self.blocked_status = self.is_blocked().into(); } pub fn unblock_all(&mut self) { - let rules = unsafe { self.policy.Rules().unwrap() }; + let policy: INetFwPolicy2 = + unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER).unwrap() }; + let rules = unsafe { policy.Rules().unwrap() }; unsafe { let result = rules.Remove(&BSTR::from(FILTER_NAME_IN)); if let Err(ref why) = result { @@ -103,15 +149,31 @@ impl GameNetworking { } } } - self.is_blocked = self.is_blocked(); + self.blocked_status = self.is_blocked().into(); } fn is_blocked(&self) -> bool { - let rules = unsafe { self.policy.Rules().unwrap() }; + let policy: INetFwPolicy2 = + unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER).unwrap() }; + let rules = unsafe { policy.Rules().unwrap() }; let in_rule_exists = unsafe { rules.Item(&BSTR::from(FILTER_NAME_IN)).is_ok() }; let out_rule_exists = unsafe { rules.Item(&BSTR::from(FILTER_NAME_OUT)).is_ok() }; in_rule_exists || out_rule_exists } + + pub fn if_failed_return_to_unblocked(&mut self) { + if self.blocked_status == BlockedStatus::Failed && !self.counting { + self.counting = true; + self.timer = Instant::now(); + } + if self.blocked_status == BlockedStatus::Failed + && self.counting + && self.timer.elapsed() >= INTERVAL + { + self.counting = false; + self.blocked_status = BlockedStatus::Unblocked; + }; + } } fn get_game_exe_path(sysinfo: &mut System) -> Option<&Path> { diff --git a/src/gui/app.rs b/src/gui/app.rs index aa5e806..ecc47ad 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -156,15 +156,12 @@ impl App { { self.game_networking.unblock_all(); } - let tint = match self.game_networking.is_blocked { - true => egui::Color32::from_hex("#f96554").unwrap(), - false => egui::Color32::from_hex("#68f954").unwrap(), - }; ui.add( egui::Image::new(egui::include_image!("../../assets/circle.svg")) .max_size([4.0, 4.0].into()) - .tint(tint), + .tint(self.game_networking.blocked_status.to_color32()), ); + self.game_networking.if_failed_return_to_unblocked(); }); }); response.response.on_disabled_hover_text( diff --git a/src/util/consts.rs b/src/util/consts.rs index d50ef32..83cd953 100644 --- a/src/util/consts.rs +++ b/src/util/consts.rs @@ -8,3 +8,10 @@ pub mod game { pub const EXE_LEGACY: &str = "GTA5.exe"; pub const WINDOW_TITLE: &str = "Grand Theft Auto V"; } + +pub mod colours { + use eframe::egui::Color32; + pub const RED: Color32 = Color32::from_rgb(249, 101, 84); + pub const YELLOW: Color32 = Color32::from_rgb(249, 236, 84); + pub const GREEN: Color32 = Color32::from_rgb(104, 249, 84); +}