Big update

This repository is no longer meant for just radarflow, thus it will be renamed.
I have split the SDK from radarflow, allowing for simpler use with new projects.
Other than that, radarflow is functionally the same.

- Fixed bug in radarflow where the entity loop didn't include the last entity.
This commit is contained in:
Janek
2023-12-30 18:07:55 +01:00
parent 462cfddfef
commit 45bba35a71
85 changed files with 6982 additions and 1036 deletions

130
csflow/src/context.rs Normal file
View File

@@ -0,0 +1,130 @@
use memflow::{plugins::{IntoProcessInstanceArcBox, Inventory, ConnectorArgs, args::Args}, os::{ModuleInfo, Os, Process}, mem::MemoryView, types::Address};
use crate::{error::Error, structs::{CPlayerController, CBaseEntity}, cs2dumper, traits::MemoryClass};
pub struct CheatCtx {
pub process: IntoProcessInstanceArcBox<'static>,
pub client_module: ModuleInfo,
pub engine_module: ModuleInfo,
}
impl CheatCtx {
fn check_version(&mut self) -> Result<(), Error> {
let game_build_number: u32 = self.process.read(self.engine_module.base + cs2dumper::offsets::engine2_dll::dwBuildNumber)?;
let offset_build_number = cs2dumper::offsets::game_info::buildNumber;
if game_build_number as usize != offset_build_number {
return Err(Error::GameVersionMismatch(game_build_number as usize, offset_build_number))
}
Ok(())
}
pub fn setup(connector: Connector, pcileech_device: String) -> Result<CheatCtx, Error> {
let inventory = Inventory::scan();
let os = {
if connector == Connector::Pcileech {
let args = Args::new()
.insert("device", &pcileech_device);
let connector_args = ConnectorArgs::new(None, args, None);
inventory.builder()
.connector(&connector.to_string())
.args(connector_args)
.os("win32")
.build()?
} else {
inventory.builder()
.connector(&connector.to_string())
.os("win32")
.build()?
}
};
let mut process = os.into_process_by_name("cs2.exe")?;
let client_module = process.module_by_name("client.dll")?;
let engine_module = process.module_by_name("engine2.dll")?;
let mut ctx = Self {
process,
client_module,
engine_module,
};
ctx.check_version()?;
Ok(ctx)
}
pub fn get_local(&mut self) -> Result<CPlayerController, Error> {
let ptr = self.process.read_addr64(self.client_module.base + cs2dumper::offsets::client_dll::dwLocalPlayerController)?;
Ok(CPlayerController::new(ptr))
}
pub fn get_plantedc4(&mut self) -> Result<CBaseEntity, Error> {
let ptr = self.process.read_addr64(self.client_module.base + cs2dumper::offsets::client_dll::dwPlantedC4)?;
let ptr2 = self.process.read_addr64(ptr)?;
Ok(CBaseEntity::new(ptr2))
}
pub fn is_bomb_planted(&mut self) -> Result<bool, Error> {
let game_rules = self.process.read_addr64(self.client_module.base + cs2dumper::offsets::client_dll::dwGameRules)?;
let data: u8 = self.process.read(game_rules + cs2dumper::client::C_CSGameRules::m_bBombPlanted)?;
Ok(data != 0)
}
pub fn is_bomb_dropped(&mut self) -> Result<bool, Error> {
let game_rules = self.process.read_addr64(self.client_module.base + cs2dumper::offsets::client_dll::dwGameRules)?;
let data: u8 = self.process.read(game_rules + cs2dumper::client::C_CSGameRules::m_bBombDropped)?;
Ok(data != 0)
}
pub fn get_entity_list(&mut self) -> Result<Address, Error> {
let ptr = self.process.read_addr64(self.client_module.base + cs2dumper::offsets::client_dll::dwEntityList)?;
Ok(ptr)
}
pub fn get_globals(&mut self) -> Result<Address, Error> {
let ptr = self.process.read_addr64(self.client_module.base + cs2dumper::offsets::client_dll::dwGlobalVars)?;
Ok(ptr)
}
pub fn map_name(&mut self, global_vars: Address) -> Result<String, Error> {
let ptr = self.process.read_addr64(global_vars + 0x188)?;
Ok(self.process.read_char_string_n(ptr, 32)?)
}
pub fn highest_entity_index(&mut self) -> Result<i32, Error> {
let game_entity_system = self.process.read_addr64(self.client_module.base + cs2dumper::offsets::client_dll::dwGameEntitySystem)?;
let highest_index = self.process.read(game_entity_system + cs2dumper::offsets::client_dll::dwGameEntitySystem_getHighestEntityIndex)?;
Ok(highest_index)
}
pub fn network_is_ingame(&mut self) -> Result<bool, Error> {
let ptr = self.process.read_addr64(self.engine_module.base + cs2dumper::offsets::engine2_dll::dwNetworkGameClient)?;
let signonstate: i32 = self.process.read(ptr + cs2dumper::offsets::engine2_dll::dwNetworkGameClient_signOnState)?;
Ok(signonstate == 6)
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum, Default)]
pub enum Connector {
#[default]
Qemu,
Kvm,
Pcileech
}
impl ToString for Connector {
fn to_string(&self) -> String {
match self {
Connector::Qemu => String::from("qemu"),
Connector::Kvm => String::from("kvm"),
Connector::Pcileech => String::from("pcileech"),
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,244 @@
/*
* Created using https://github.com/a2x/cs2-dumper
* Sat, 30 Dec 2023 03:17:25 +0000
*/
#![allow(non_snake_case, non_upper_case_globals)]
pub mod CEmptyEntityInstance {
}
pub mod CEntityComponent {
}
pub mod CEntityComponentHelper {
pub const m_flags: usize = 0x8; // uint32_t
pub const m_pInfo: usize = 0x10; // EntComponentInfo_t*
pub const m_nPriority: usize = 0x18; // int32_t
pub const m_pNext: usize = 0x20; // CEntityComponentHelper*
}
pub mod CEntityIOOutput {
pub const m_Value: usize = 0x18; // CVariantBase<CVariantDefaultAllocator>
}
pub mod CEntityIdentity {
pub const m_nameStringableIndex: usize = 0x14; // int32_t
pub const m_name: usize = 0x18; // CUtlSymbolLarge
pub const m_designerName: usize = 0x20; // CUtlSymbolLarge
pub const m_flags: usize = 0x30; // uint32_t
pub const m_worldGroupId: usize = 0x38; // WorldGroupId_t
pub const m_fDataObjectTypes: usize = 0x3C; // uint32_t
pub const m_PathIndex: usize = 0x40; // ChangeAccessorFieldPathIndex_t
pub const m_pPrev: usize = 0x58; // CEntityIdentity*
pub const m_pNext: usize = 0x60; // CEntityIdentity*
pub const m_pPrevByClass: usize = 0x68; // CEntityIdentity*
pub const m_pNextByClass: usize = 0x70; // CEntityIdentity*
}
pub mod CEntityInstance {
pub const m_iszPrivateVScripts: usize = 0x8; // CUtlSymbolLarge
pub const m_pEntity: usize = 0x10; // CEntityIdentity*
pub const m_CScriptComponent: usize = 0x28; // CScriptComponent*
}
pub mod CNetworkVarChainer {
pub const m_PathIndex: usize = 0x20; // ChangeAccessorFieldPathIndex_t
}
pub mod CScriptComponent { // CEntityComponent
pub const m_scriptClassName: usize = 0x30; // CUtlSymbolLarge
}
pub mod CVariantDefaultAllocator {
}
pub mod EngineLoopState_t {
pub const m_nPlatWindowWidth: usize = 0x18; // int32_t
pub const m_nPlatWindowHeight: usize = 0x1C; // int32_t
pub const m_nRenderWidth: usize = 0x20; // int32_t
pub const m_nRenderHeight: usize = 0x24; // int32_t
}
pub mod EntComponentInfo_t {
pub const m_pName: usize = 0x0; // char*
pub const m_pCPPClassname: usize = 0x8; // char*
pub const m_pNetworkDataReferencedDescription: usize = 0x10; // char*
pub const m_pNetworkDataReferencedPtrPropDescription: usize = 0x18; // char*
pub const m_nRuntimeIndex: usize = 0x20; // int32_t
pub const m_nFlags: usize = 0x24; // uint32_t
pub const m_pBaseClassComponentHelper: usize = 0x60; // CEntityComponentHelper*
}
pub mod EntInput_t {
}
pub mod EntOutput_t {
}
pub mod EventAdvanceTick_t { // EventSimulate_t
pub const m_nCurrentTick: usize = 0x30; // int32_t
pub const m_nCurrentTickThisFrame: usize = 0x34; // int32_t
pub const m_nTotalTicksThisFrame: usize = 0x38; // int32_t
pub const m_nTotalTicks: usize = 0x3C; // int32_t
}
pub mod EventAppShutdown_t {
pub const m_nDummy0: usize = 0x0; // int32_t
}
pub mod EventClientAdvanceTick_t { // EventAdvanceTick_t
}
pub mod EventClientFrameSimulate_t {
pub const m_LoopState: usize = 0x0; // EngineLoopState_t
pub const m_flRealTime: usize = 0x28; // float
pub const m_flFrameTime: usize = 0x2C; // float
}
pub mod EventClientOutput_t {
pub const m_LoopState: usize = 0x0; // EngineLoopState_t
pub const m_flRenderTime: usize = 0x28; // float
pub const m_flRealTime: usize = 0x2C; // float
pub const m_flRenderFrameTimeUnbounded: usize = 0x30; // float
pub const m_bRenderOnly: usize = 0x34; // bool
}
pub mod EventClientPauseSimulate_t { // EventSimulate_t
}
pub mod EventClientPollInput_t {
pub const m_LoopState: usize = 0x0; // EngineLoopState_t
pub const m_flRealTime: usize = 0x28; // float
}
pub mod EventClientPollNetworking_t {
pub const m_nTickCount: usize = 0x0; // int32_t
}
pub mod EventClientPostAdvanceTick_t { // EventPostAdvanceTick_t
}
pub mod EventClientPostOutput_t {
pub const m_LoopState: usize = 0x0; // EngineLoopState_t
pub const m_flRenderTime: usize = 0x28; // double
pub const m_flRenderFrameTime: usize = 0x30; // float
pub const m_flRenderFrameTimeUnbounded: usize = 0x34; // float
pub const m_bRenderOnly: usize = 0x38; // bool
}
pub mod EventClientPostSimulate_t { // EventSimulate_t
}
pub mod EventClientPreOutput_t {
pub const m_LoopState: usize = 0x0; // EngineLoopState_t
pub const m_flRenderTime: usize = 0x28; // double
pub const m_flRenderFrameTime: usize = 0x30; // double
pub const m_flRenderFrameTimeUnbounded: usize = 0x38; // double
pub const m_flRealTime: usize = 0x40; // float
pub const m_bRenderOnly: usize = 0x44; // bool
}
pub mod EventClientPreSimulate_t { // EventSimulate_t
}
pub mod EventClientPredictionPostNetupdate_t {
}
pub mod EventClientProcessGameInput_t {
pub const m_LoopState: usize = 0x0; // EngineLoopState_t
pub const m_flRealTime: usize = 0x28; // float
pub const m_flFrameTime: usize = 0x2C; // float
}
pub mod EventClientProcessInput_t {
pub const m_LoopState: usize = 0x0; // EngineLoopState_t
pub const m_flRealTime: usize = 0x28; // float
pub const m_flTickInterval: usize = 0x2C; // float
pub const m_flTickStartTime: usize = 0x30; // double
}
pub mod EventClientProcessNetworking_t {
}
pub mod EventClientSceneSystemThreadStateChange_t {
pub const m_bThreadsActive: usize = 0x0; // bool
}
pub mod EventClientSendInput_t {
pub const m_bFinalClientCommandTick: usize = 0x0; // bool
pub const m_nAdditionalClientCommandsToCreate: usize = 0x4; // int32_t
}
pub mod EventClientSimulate_t { // EventSimulate_t
}
pub mod EventFrameBoundary_t {
pub const m_flFrameTime: usize = 0x0; // float
}
pub mod EventModInitialized_t {
}
pub mod EventPostAdvanceTick_t { // EventSimulate_t
pub const m_nCurrentTick: usize = 0x30; // int32_t
pub const m_nCurrentTickThisFrame: usize = 0x34; // int32_t
pub const m_nTotalTicksThisFrame: usize = 0x38; // int32_t
pub const m_nTotalTicks: usize = 0x3C; // int32_t
}
pub mod EventPostDataUpdate_t {
pub const m_nCount: usize = 0x0; // int32_t
}
pub mod EventPreDataUpdate_t {
pub const m_nCount: usize = 0x0; // int32_t
}
pub mod EventProfileStorageAvailable_t {
pub const m_nSplitScreenSlot: usize = 0x0; // CSplitScreenSlot
}
pub mod EventServerAdvanceTick_t { // EventAdvanceTick_t
}
pub mod EventServerPollNetworking_t { // EventSimulate_t
}
pub mod EventServerPostAdvanceTick_t { // EventPostAdvanceTick_t
}
pub mod EventServerPostSimulate_t { // EventSimulate_t
}
pub mod EventServerProcessNetworking_t { // EventSimulate_t
}
pub mod EventServerSimulate_t { // EventSimulate_t
}
pub mod EventSetTime_t {
pub const m_LoopState: usize = 0x0; // EngineLoopState_t
pub const m_nClientOutputFrames: usize = 0x28; // int32_t
pub const m_flRealTime: usize = 0x30; // double
pub const m_flRenderTime: usize = 0x38; // double
pub const m_flRenderFrameTime: usize = 0x40; // double
pub const m_flRenderFrameTimeUnbounded: usize = 0x48; // double
pub const m_flRenderFrameTimeUnscaled: usize = 0x50; // double
pub const m_flTickRemainder: usize = 0x58; // double
}
pub mod EventSimpleLoopFrameUpdate_t {
pub const m_LoopState: usize = 0x0; // EngineLoopState_t
pub const m_flRealTime: usize = 0x28; // float
pub const m_flFrameTime: usize = 0x2C; // float
}
pub mod EventSimulate_t {
pub const m_LoopState: usize = 0x0; // EngineLoopState_t
pub const m_bFirstTick: usize = 0x28; // bool
pub const m_bLastTick: usize = 0x29; // bool
}
pub mod EventSplitScreenStateChanged_t {
}

View File

@@ -0,0 +1,4 @@
#![allow(dead_code)]
pub mod client;
pub mod engine2;
pub mod offsets;

View File

@@ -0,0 +1,51 @@
/*
* Created using https://github.com/a2x/cs2-dumper
* Sat, 30 Dec 2023 03:17:26 +0000
*/
#![allow(non_snake_case, non_upper_case_globals)]
pub mod client_dll { // client.dll
pub const dwEntityList: usize = 0x17C1950;
pub const dwForceAttack: usize = 0x16C1E70;
pub const dwForceAttack2: usize = 0x16C1F00;
pub const dwForceBackward: usize = 0x16C2140;
pub const dwForceCrouch: usize = 0x16C2410;
pub const dwForceForward: usize = 0x16C20B0;
pub const dwForceJump: usize = 0x16C2380;
pub const dwForceLeft: usize = 0x16C21D0;
pub const dwForceRight: usize = 0x16C2260;
pub const dwGameEntitySystem: usize = 0x18ED250;
pub const dwGameEntitySystem_getHighestEntityIndex: usize = 0x1510;
pub const dwGameRules: usize = 0x181E048;
pub const dwGlobalVars: usize = 0x16BDC98;
pub const dwGlowManager: usize = 0x181D7B0;
pub const dwInterfaceLinkList: usize = 0x191AEE8;
pub const dwLocalPlayerController: usize = 0x1810F48;
pub const dwLocalPlayerPawn: usize = 0x16C8F38;
pub const dwPlantedC4: usize = 0x1824A88;
pub const dwPrediction: usize = 0x16C8E00;
pub const dwSensitivity: usize = 0x181ED48;
pub const dwSensitivity_sensitivity: usize = 0x40;
pub const dwViewAngles: usize = 0x1880DC0;
pub const dwViewMatrix: usize = 0x1820150;
pub const dwViewRender: usize = 0x1820998;
}
pub mod engine2_dll { // engine2.dll
pub const dwBuildNumber: usize = 0x4E03D4;
pub const dwNetworkGameClient: usize = 0x4DF988;
pub const dwNetworkGameClient_getLocalPlayer: usize = 0xF0;
pub const dwNetworkGameClient_maxClients: usize = 0x250;
pub const dwNetworkGameClient_signOnState: usize = 0x240;
pub const dwWindowHeight: usize = 0x596E1C;
pub const dwWindowWidth: usize = 0x596E18;
}
pub mod game_info { // Some additional information about the game at dump time
pub const buildNumber: usize = 0x369F; // Game build number
}
pub mod inputsystem_dll { // inputsystem.dll
pub const dwInputSystem: usize = 0x35760;
}

5
csflow/src/enums/mod.rs Normal file
View File

@@ -0,0 +1,5 @@
mod player_type;
mod teamid;
pub use player_type::PlayerType;
pub use teamid::TeamID;

View File

@@ -0,0 +1,9 @@
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Default, PartialEq)]
pub enum PlayerType {
#[default]
Unknown,
Spectator,
Local,
Enemy,
Team
}

View File

@@ -0,0 +1,7 @@
#[repr(i32)]
#[derive(Debug, Eq, PartialEq, enum_primitive_derive::Primitive)]
pub enum TeamID {
Spectator = 1,
T = 2,
CT = 3
}

33
csflow/src/error.rs Normal file
View File

@@ -0,0 +1,33 @@
use memflow::types::Address;
use thiserror::Error;
use crate::structs::Vec3;
#[derive(Error, Debug)]
pub enum Error {
/// Game version mismatch.
/// First arg is the game version, second is the offset version.
#[error("version mismatch, game has version {0}, but offsets have version {1}")]
GameVersionMismatch(usize, usize),
#[error("memflow error: {0}")]
Memflow(#[from] memflow::error::Error),
#[error("memflow partial error when reading address: {0}")]
MemflowPartialAddress(#[from] memflow::error::PartialError<Address>),
#[error("memflow partial error when reading Vec3: {0}")]
MemflowPartialVec3(#[from] memflow::error::PartialError<Vec3>),
#[error("memflow partial error when reading String: {0}")]
MemflowPartialString(#[from] memflow::error::PartialError<String>),
#[error("memflow partial error when reading i32: {0}")]
MemflowPartiali32(#[from] memflow::error::PartialError<i32>),
#[error("memflow partial error when reading u32: {0}")]
MemflowPartialu32(#[from] memflow::error::PartialError<u32>),
#[error("memflow partial error when reading u8: {0}")]
MemflowPartialu8(#[from] memflow::error::PartialError<u8>)
}

14
csflow/src/lib.rs Normal file
View File

@@ -0,0 +1,14 @@
#![allow(dead_code)]
mod cs2dumper;
mod context;
mod error;
pub mod structs;
pub mod traits;
pub mod enums;
pub use context::*;
pub use error::Error;
pub use memflow::prelude as memflow;

View File

@@ -0,0 +1,47 @@
use memflow::{types::Address, mem::MemoryView};
use crate::{CheatCtx, Error, cs2dumper, traits::{BaseEntity, MemoryClass}, structs::Vec3};
pub struct CBaseEntity(Address);
impl MemoryClass for CBaseEntity {
fn ptr(&self) -> Address {
self.0
}
fn new(ptr: Address) -> Self {
Self(ptr)
}
}
impl BaseEntity for CBaseEntity {
fn from_index(ctx: &mut CheatCtx, entity_list: Address, index: i32) -> Result<Option<CBaseEntity>, Error> {
let list_entry = ctx.process.read_addr64(entity_list + 8 * (index >> 9) + 16)?;
if list_entry.is_null() && !list_entry.is_valid() {
return Ok(None);
}
let player_ptr = ctx.process.read_addr64(list_entry + 120 * (index & 0x1FF))?;
if player_ptr.is_null() && !player_ptr.is_valid() {
return Ok(None);
}
Ok(Some(Self::new(player_ptr)))
}
fn pos(&self, ctx: &mut CheatCtx) -> Result<Vec3, Error> {
let node = ctx.process.read_addr64(self.0 + cs2dumper::client::C_BaseEntity::m_pGameSceneNode)?;
Ok(ctx.process.read(node + cs2dumper::client::CGameSceneNode::m_vecAbsOrigin)?)
}
fn class_name(&self, ctx: &mut CheatCtx) -> Result<String, Error> {
let entity_identity_ptr = ctx.process.read_addr64(self.0 + cs2dumper::client::CEntityInstance::m_pEntity)?;
let class_name_ptr = ctx.process.read_addr64(entity_identity_ptr + cs2dumper::client::CEntityIdentity::m_designerName)?;
Ok(ctx.process.read_char_string_n(class_name_ptr, 32)?)
}
}
impl CBaseEntity {
pub fn to_player_controller(&self) -> super::CPlayerController {
super::CPlayerController::new(self.0)
}
}

View File

@@ -0,0 +1,7 @@
mod base_entity;
mod player_controller;
mod player_pawn;
pub use base_entity::CBaseEntity;
pub use player_controller::CPlayerController;
pub use player_pawn::CPlayerPawn;

View File

@@ -0,0 +1,88 @@
use memflow::{types::Address, mem::MemoryView};
use num_traits::FromPrimitive;
use crate::{CheatCtx, Error, cs2dumper, structs::Vec3, traits::{MemoryClass, BaseEntity}, enums::{TeamID, PlayerType}};
pub struct CPlayerController(Address);
impl MemoryClass for CPlayerController {
fn ptr(&self) -> Address {
self.0
}
fn new(ptr: Address) -> Self {
Self(ptr)
}
}
impl BaseEntity for CPlayerController {
fn from_index(ctx: &mut CheatCtx, entity_list: Address, index: i32) -> Result<Option<CPlayerController>, Error> {
let list_entry = ctx.process.read_addr64(entity_list + 8 * (index >> 9) + 16)?;
if list_entry.is_null() && !list_entry.is_valid() {
return Ok(None);
}
let player_ptr = ctx.process.read_addr64(list_entry + 120 * (index & 0x1FF))?;
if player_ptr.is_null() && !player_ptr.is_valid() {
return Ok(None);
}
Ok(Some(Self::new(player_ptr)))
}
fn pos(&self, ctx: &mut CheatCtx) -> Result<Vec3, Error> {
let node = ctx.process.read_addr64(self.0 + cs2dumper::client::C_BaseEntity::m_pGameSceneNode)?;
Ok(ctx.process.read(node + cs2dumper::client::CGameSceneNode::m_vecAbsOrigin)?)
}
fn class_name(&self, ctx: &mut CheatCtx) -> Result<String, Error> {
let entity_identity_ptr = ctx.process.read_addr64(self.0 + cs2dumper::client::CEntityInstance::m_pEntity)?;
let class_name_ptr = ctx.process.read_addr64(entity_identity_ptr + cs2dumper::client::CEntityIdentity::m_designerName)?;
Ok(ctx.process.read_char_string_n(class_name_ptr, 32)?)
}
}
impl CPlayerController {
pub fn get_pawn(&self, ctx: &mut CheatCtx, entity_list: Address) -> Result<Option<super::CPlayerPawn>, Error> {
let uhandle = ctx.process.read(self.0 + cs2dumper::client::CCSPlayerController::m_hPlayerPawn)?;
super::CPlayerPawn::from_uhandle(ctx, entity_list, uhandle)
}
// Technically something that should be in the BaseEntity trait, but we are only gonna use it on CPlayerController
pub fn get_team(&self, ctx: &mut CheatCtx) -> Result<Option<TeamID>, Error> {
let team_num: i32 = ctx.process.read(self.0 + cs2dumper::client::C_BaseEntity::m_iTeamNum)?;
Ok(TeamID::from_i32(team_num))
}
pub fn get_player_type(&self, ctx: &mut CheatCtx, local: &CPlayerController) -> Result<Option<PlayerType>, Error> {
if self.0 == local.0 {
return Ok(Some(PlayerType::Local))
}
let team = {
match self.get_team(ctx)? {
Some(t) => t,
None => { return Ok(None) },
}
};
let local_team = {
match local.get_team(ctx)? {
Some(t) => t,
None => { return Ok(None) },
}
};
let player_type = {
if team == TeamID::Spectator {
PlayerType::Spectator
} else if team != local_team {
PlayerType::Enemy
} else {
PlayerType::Team
}
};
Ok(Some(player_type))
}
}

View File

@@ -0,0 +1,80 @@
use memflow::{types::Address, mem::MemoryView};
use crate::{Error, CheatCtx, cs2dumper, structs::Vec3, traits::MemoryClass};
pub struct CPlayerPawn(Address);
impl MemoryClass for CPlayerPawn {
fn ptr(&self) -> Address {
self.0
}
fn new(ptr: Address) -> Self {
Self(ptr)
}
}
impl CPlayerPawn {
pub fn from_uhandle(ctx: &mut CheatCtx, entity_list: Address, uhandle: u32) -> Result<Option<Self>, Error> {
let list_entry = ctx.process.read_addr64(entity_list + 0x8 * ((uhandle & 0x7FFF) >> 9) + 16)?;
if list_entry.is_null() || !list_entry.is_valid() {
Ok(None)
} else {
let ptr = ctx.process.read_addr64(list_entry + 120 * (uhandle & 0x1FF))?;
Ok(Some(Self(ptr)))
}
}
// Todo: Optimize this function: find another way to do this
pub fn has_c4(&self, ctx: &mut CheatCtx, entity_list: Address) -> Result<bool, Error> {
let mut has_c4 = false;
let wep_services = ctx.process.read_addr64(self.0 + cs2dumper::client::C_BasePlayerPawn::m_pWeaponServices)?;
let wep_count: i32 = ctx.process.read(wep_services + cs2dumper::client::CPlayer_WeaponServices::m_hMyWeapons)?;
let wep_base = ctx.process.read_addr64(wep_services + cs2dumper::client::CPlayer_WeaponServices::m_hMyWeapons + 0x8)?;
for wep_idx in 0..wep_count {
let handle: i32 = ctx.process.read(wep_base + wep_idx * 0x4)?;
if handle == -1 {
continue;
}
let list_entry = ctx.process.read_addr64(entity_list + 0x8 * ((handle & 0x7FFF) >> 9) + 16)?;
if let Some(wep_ptr) = {
if list_entry.is_null() || !list_entry.is_valid() {
None
} else {
let ptr = ctx.process.read_addr64(list_entry + 120 * (handle & 0x1FF))?;
Some(ptr)
}
} {
let wep_data = ctx.process.read_addr64(wep_ptr + cs2dumper::client::C_BaseEntity::m_nSubclassID + 0x8)?;
let id: i32 = ctx.process.read(wep_data + cs2dumper::client::CCSWeaponBaseVData::m_WeaponType)?;
if id == 7 {
has_c4 = true;
break;
}
}
}
Ok(has_c4)
}
pub fn pos(&self, ctx: &mut CheatCtx) -> Result<Vec3, Error> {
Ok(ctx.process.read(self.0 + cs2dumper::client::C_BasePlayerPawn::m_vOldOrigin)?)
}
pub fn angles(&self, ctx: &mut CheatCtx) -> Result<Vec3, Error> {
Ok(ctx.process.read(self.0 + cs2dumper::client::C_CSPlayerPawnBase::m_angEyeAngles)?)
}
pub fn health(&self, ctx: &mut CheatCtx) -> Result<u32, Error> {
Ok(ctx.process.read(self.0 + cs2dumper::client::C_BaseEntity::m_iHealth)?)
}
/// Same as ::get_health > 0
pub fn is_alive(&self, ctx: &mut CheatCtx) -> Result<bool, Error> {
Ok(self.health(ctx)? > 0)
}
}

View File

@@ -0,0 +1,5 @@
mod vec3;
mod entity;
pub use vec3::Vec3;
pub use entity::{CBaseEntity, CPlayerController, CPlayerPawn};

View File

@@ -0,0 +1,11 @@
use serde::Serialize;
#[derive(Debug, Clone, Copy, Serialize, serde::Deserialize)]
#[repr(C)]
pub struct Vec3 {
pub x: f32,
pub y: f32,
pub z: f32
}
unsafe impl dataview::Pod for Vec3 {}

View File

@@ -0,0 +1,11 @@
use memflow::types::Address;
use crate::{CheatCtx, structs::Vec3, Error};
/// A trait for basic functions from C_BaseEntity
/// CCSPlayerController inherits C_BaseEntity, which is why this trait exists.
pub trait BaseEntity {
fn from_index(ctx: &mut CheatCtx, entity_list: Address, index: i32) -> Result<Option<Self>, Error> where Self: std::marker::Sized;
fn pos(&self, ctx: &mut CheatCtx) -> Result<Vec3, Error>;
fn class_name(&self, ctx: &mut CheatCtx) -> Result<String, Error>;
}

View File

@@ -0,0 +1,7 @@
use memflow::types::Address;
/// A trait that implements basic functions for a class represented by a single pointer
pub trait MemoryClass {
fn ptr(&self) -> Address;
fn new(ptr: Address) -> Self;
}

5
csflow/src/traits/mod.rs Normal file
View File

@@ -0,0 +1,5 @@
mod base_entity;
mod memory_class;
pub use base_entity::BaseEntity;
pub use memory_class::MemoryClass;