From 10da0883a10be097ad3fdf1819ee64ecd64c0259 Mon Sep 17 00:00:00 2001
From: Janek <development@superyu.xyz>
Date: Wed, 17 Apr 2024 18:55:46 +0200
Subject: [PATCH] feat: faster bomb holder search

Fully batched bomb holder search
---
 src/dma/context/mod.rs    | 94 ++++++++++++++++++++++++++++++++++++++-
 src/dma/mod.rs            | 48 ++++++--------------
 src/dma/threaddata/mod.rs |  2 +-
 3 files changed, 108 insertions(+), 36 deletions(-)

diff --git a/src/dma/context/mod.rs b/src/dma/context/mod.rs
index 8ee5e75..755bc4b 100755
--- a/src/dma/context/mod.rs
+++ b/src/dma/context/mod.rs
@@ -144,6 +144,97 @@ impl DmaCtx {
         Ok(is_controller)
     }
 
+    pub fn get_c4_holder(&mut self, pawns: Vec<Address>, entity_list: Address) -> Option<Address> {
+        // (pawn, wep_services, wep_count, wep_base)
+        let mut data_vec: Vec<(Address, u64, i32, u64)> = pawns
+            .into_iter()
+            .map(|pawn| (pawn, 0u64, 0i32, 0u64))
+            .collect();
+
+        // Get wep_services
+        let mut batcher = self.process.batcher();
+        data_vec.iter_mut().for_each(|(pawn, wep_services, _, _)| {
+            batcher.read_into(*pawn + cs2dumper::client::C_BasePlayerPawn::m_pWeaponServices, wep_services);
+        });
+        drop(batcher);
+
+        // Get wep_count and wep_base
+        let mut batcher = self.process.batcher();
+        data_vec.iter_mut().for_each(|(_, wep_services, wep_count, wep_base)| {
+            batcher.read_into((*wep_services + cs2dumper::client::CPlayer_WeaponServices::m_hMyWeapons as u64).into(), wep_count);
+            batcher.read_into((*wep_services + cs2dumper::client::CPlayer_WeaponServices::m_hMyWeapons as u64 + 0x8).into() , wep_base);
+        });
+        drop(batcher);
+
+        // Rebuild data vec
+        // Vec<(pawn, wep_base, Vec<(buff, buff2)>)>
+        let mut data_vec: Vec<(Address, u64, Vec<(u64, i32)>)> = data_vec
+            .into_iter()
+            .map(|(pawn, _, wep_count, wep_base)| {
+                let weps = (0..wep_count).into_iter().map(|idx| (0u64, idx)).collect();
+                (pawn, wep_base, weps)
+            })
+            .collect();
+
+        // Get handle
+        let mut batcher = self.process.batcher();
+        data_vec.iter_mut().for_each(|(_, wep_base, wep_data_vec)| {
+            wep_data_vec.iter_mut().for_each(|(_, idx)| {
+                let b: Address = (*wep_base).into();
+                batcher.read_into(b + * idx * 0x4, idx);
+            });
+        });
+        drop(batcher);
+
+        // Get list entry
+        let mut batcher = self.process.batcher();
+        data_vec.iter_mut().for_each(|(_, _, wep_data_vec)| {
+            wep_data_vec.iter_mut().for_each(|(list_entry, handle)| {
+                batcher.read_into(entity_list + 0x8 * ((*handle & 0x7FFF) >> 9) + 16, list_entry);
+            });
+        });
+        drop(batcher);
+
+        // Get wep ptr
+        let mut batcher = self.process.batcher();
+        data_vec.iter_mut().for_each(|(_, _, wep_data_vec)| {
+            wep_data_vec.iter_mut().for_each(|(list_entry, handle)| {
+                let le: Address = (*list_entry).into();
+                batcher.read_into(le + 120 * (*handle & 0x1FF), list_entry);
+            });
+        });
+        drop(batcher);
+
+        // Get wep data
+        let mut batcher = self.process.batcher();
+        data_vec.iter_mut().for_each(|(_, _, wep_data_vec)| {
+            wep_data_vec.iter_mut().for_each(|(wep_ptr, _)| {
+                let b: Address = (*wep_ptr).into();
+                batcher.read_into(b + cs2dumper::client::C_BaseEntity::m_nSubclassID + 0x8, wep_ptr);
+            });
+        });
+        drop(batcher);
+
+        // Get wep id
+        let mut batcher = self.process.batcher();
+        data_vec.iter_mut().for_each(|(_, _, wep_data_vec)| {
+            wep_data_vec.iter_mut().for_each(|(wep_data, id)| {
+                let b: Address = (*wep_data).into();
+                batcher.read_into(b + cs2dumper::client::CCSWeaponBaseVData::m_WeaponType, id);
+            });
+        });
+        drop(batcher);
+
+        let holder = data_vec.into_iter().find(|(_, _, wep_data_vec)| {
+            wep_data_vec.iter().find(|(_, id)| { *id == 7 }).is_some()
+        });
+
+        match holder {
+            Some((addr, _, _)) => Some(addr),
+            None => None,
+        }
+    }
+
     // Todo: Optimize this function: find another way to do this
     pub fn has_c4(&mut self, pawn: Address, entity_list: Address) -> anyhow::Result<bool> {
         let mut has_c4 = false;
@@ -165,7 +256,8 @@ impl DmaCtx {
                     let ptr = self.process.read_addr64(list_entry + 120 * (handle & 0x1FF))?;
                     Some(ptr)
                 }
-            } {
+            }
+            {
                 let wep_data = self.process.read_addr64(wep_ptr + cs2dumper::client::C_BaseEntity::m_nSubclassID + 0x8)?;
                 let id: i32 = self.process.read(wep_data + cs2dumper::client::CCSWeaponBaseVData::m_WeaponType)?;
 
diff --git a/src/dma/mod.rs b/src/dma/mod.rs
index 5b4da83..5e926e7 100755
--- a/src/dma/mod.rs
+++ b/src/dma/mod.rs
@@ -1,8 +1,8 @@
 use std::{thread, time::{Duration, Instant}};
 
-use memflow::{os::Process, types::Address, mem::MemoryView};
+use memflow::{os::Process, mem::MemoryView};
 
-use crate::{enums::{TeamID, PlayerType}, comms::{EntityData, PlayerData, RadarData, ArcRwlockRadarData, BombData}};
+use crate::{enums::PlayerType, comms::{EntityData, PlayerData, RadarData, ArcRwlockRadarData, BombData}};
 
 use self::{context::DmaCtx, threaddata::CsData};
 
@@ -54,6 +54,12 @@ pub async fn run(radar_data: ArcRwlockRadarData, connector: Connector, pcileech_
             data.recheck_bomb_holder = true;
         }
 
+        if data.recheck_bomb_holder {
+            let pawns = data.players.clone().into_iter().map(|(_, pawn)| pawn).collect();
+            data.bomb_holder = ctx.get_c4_holder(pawns, data.entity_list.into());
+            data.recheck_bomb_holder = false;
+        }
+
         let bomb_defuse_timeleft: f32 = {
             if data.bomb_planted && !data.bomb_exploded && !data.bomb_defused {
                 if let Some(bomb_stamp) = data.bomb_planted_stamp {
@@ -123,23 +129,9 @@ pub async fn run(radar_data: ArcRwlockRadarData, connector: Connector, pcileech_
             ).unwrap();
 
             if local_data.health > 0 {
-                let has_bomb = {
-                    if data.bomb_planted || data.bomb_dropped {
-                        false
-                    } else if data.recheck_bomb_holder {
-                        if local_data.team == Some(TeamID::T) && !data.bomb_dropped && !data.bomb_planted {
-                            let is_holder = ctx.has_c4(
-                                data.local_pawn.into(), data.entity_list.into()
-                            ).unwrap_or(false);
-
-                            if is_holder {
-                                data.bomb_holder = data.local.into();
-                                data.recheck_bomb_holder = false;
-                            }
-
-                            is_holder
-                        } else { false }
-                    } else { Address::from(data.local) == data.bomb_holder }
+                let has_bomb = match data.bomb_holder {
+                    Some(bh) => data.local_pawn == bh.to_umem(),
+                    None => false,
                 };
 
                 entity_data.push(
@@ -164,21 +156,9 @@ pub async fn run(radar_data: ArcRwlockRadarData, connector: Connector, pcileech_
                     continue;
                 }
 
-                let has_bomb = {
-                    if data.bomb_planted {
-                        false
-                    } else if data.recheck_bomb_holder {
-                        if player_data.team == Some(TeamID::T) && !data.bomb_dropped && !data.bomb_planted {
-                            let is_holder = ctx.has_c4(*pawn, data.entity_list.into()).unwrap_or(false);
-
-                            if is_holder {
-                                data.bomb_holder = *controller;
-                                data.recheck_bomb_holder = false;
-                            }
-
-                            is_holder
-                        } else { false }
-                    } else { *controller == data.bomb_holder }
+                let has_bomb = match data.bomb_holder {
+                    Some(bh) => *pawn == bh,
+                    None => false,
                 };
 
                 let player_type = {
diff --git a/src/dma/threaddata/mod.rs b/src/dma/threaddata/mod.rs
index efdf66f..40df3c0 100755
--- a/src/dma/threaddata/mod.rs
+++ b/src/dma/threaddata/mod.rs
@@ -9,7 +9,7 @@ pub struct CsData {
     // Entities
     pub players: Vec<(Address, Address)>,
     pub bomb: Address,
-    pub bomb_holder: Address,
+    pub bomb_holder: Option<Address>,
     pub recheck_bomb_holder: bool,
 
     // Pointers