csflow:
 - Create structs for gamerules and global vars

radarflow:
 - new dma loop with less frequent cache invalidation
 - The new loop tries to run at a fixed 128 hz. Thats the max tickrate in cs2. The data is also only updated when a tick change is detected, so that should keep data fetching to a minimum.
 - todo: more testing for cache invalidation
This commit is contained in:
Janek
2023-12-31 04:32:12 +01:00
parent 0f0f7232fb
commit 7c652cb984
15 changed files with 305 additions and 175 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "csflow"
version = "0.1.1"
version = "0.1.2"
authors = ["Janek S <development@superyu.xyz>"]
edition = "2021"
description = "SDK for CS2 cheats utilizing memflow"

View File

@@ -1,6 +1,6 @@
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};
use crate::{error::Error, structs::{CPlayerController, CBaseEntity, GlobalVars, GameRules}, cs2dumper, traits::MemoryClass};
pub struct CheatCtx {
pub process: IntoProcessInstanceArcBox<'static>,
@@ -71,39 +71,30 @@ impl CheatCtx {
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_globals(&mut self) -> Result<GlobalVars, Error> {
let ptr = self.process.read_addr64(self.client_module.base + cs2dumper::offsets::client_dll::dwGlobalVars)?;
Ok(GlobalVars::new(ptr))
}
pub fn get_gamerules(&mut self) -> Result<GameRules, Error> {
let ptr = self.process.read_addr64(self.client_module.base + cs2dumper::offsets::client_dll::dwGameRules)?;
Ok(GameRules::new(ptr))
}
// todo: seperate into own class
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)?)
}
// todo: seperate into own class
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)
}
// todo: seperate into own class
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)?;

View File

@@ -28,6 +28,9 @@ pub enum Error {
#[error("memflow partial error when reading u32: {0}")]
MemflowPartialu32(#[from] memflow::error::PartialError<u32>),
#[error("memflow partial error when reading f32: {0}")]
MemflowPartialf32(#[from] memflow::error::PartialError<f32>),
#[error("memflow partial error when reading u8: {0}")]
MemflowPartialu8(#[from] memflow::error::PartialError<u8>)
}

View File

@@ -1,6 +1,7 @@
use memflow::{types::Address, mem::MemoryView};
use crate::{CheatCtx, Error, cs2dumper, traits::{BaseEntity, MemoryClass}, structs::Vec3};
#[derive(Debug, Clone, Copy)]
pub struct CBaseEntity(Address);
impl MemoryClass for CBaseEntity {

View File

@@ -3,6 +3,7 @@ use num_traits::FromPrimitive;
use crate::{CheatCtx, Error, cs2dumper, structs::Vec3, traits::{MemoryClass, BaseEntity}, enums::{TeamID, PlayerType}};
#[derive(Debug, Clone, Copy)]
pub struct CPlayerController(Address);
impl MemoryClass for CPlayerController {

View File

@@ -2,6 +2,7 @@ use memflow::{types::Address, mem::MemoryView};
use crate::{Error, CheatCtx, cs2dumper, structs::Vec3, traits::MemoryClass};
#[derive(Debug, Clone, Copy)]
pub struct CPlayerPawn(Address);
impl MemoryClass for CPlayerPawn {

View File

@@ -0,0 +1,35 @@
use memflow::{types::Address, mem::MemoryView};
use crate::{traits::MemoryClass, CheatCtx, Error, cs2dumper};
#[derive(Debug, Clone, Copy)]
pub struct GameRules(Address);
impl MemoryClass for GameRules {
fn ptr(&self) -> memflow::types::Address {
self.0
}
fn new(ptr: memflow::types::Address) -> Self {
Self(ptr)
}
}
impl GameRules {
pub fn bomb_dropped(&self, ctx: &mut CheatCtx) -> Result<bool, Error> {
let data: u8 = ctx.process.read(self.0 + cs2dumper::client::C_CSGameRules::m_bBombDropped)?;
Ok(data != 0)
}
pub fn total_rounds_played(&self, ctx: &mut CheatCtx) -> Result<i32, Error> {
Ok(ctx.process.read(self.0 + cs2dumper::client::C_CSGameRules::m_totalRoundsPlayed)?)
}
pub fn game_phase(&self, ctx: &mut CheatCtx) -> Result<i32, Error> {
Ok(ctx.process.read(self.0 + cs2dumper::client::C_CSGameRules::m_gamePhase)?)
}
pub fn bomb_planted(&self, ctx: &mut CheatCtx) -> Result<bool, Error> {
let data: u8 = ctx.process.read(self.0 + cs2dumper::client::C_CSGameRules::m_bBombPlanted)?;
Ok(data != 0)
}
}

View File

@@ -0,0 +1,67 @@
use memflow::{types::Address, mem::MemoryView};
use crate::{traits::MemoryClass, CheatCtx, Error};
#[derive(Debug, Clone, Copy)]
pub struct GlobalVars(Address);
impl MemoryClass for GlobalVars {
fn ptr(&self) -> memflow::types::Address {
self.0
}
fn new(ptr: memflow::types::Address) -> Self {
Self(ptr)
}
}
impl GlobalVars {
pub fn real_time(&self, ctx: &mut CheatCtx) -> Result<f32, Error> {
Ok(ctx.process.read(self.0)?)
}
pub fn frame_count(&self, ctx: &mut CheatCtx) -> Result<i32, Error> {
Ok(ctx.process.read(self.0 + 0x4)?)
}
pub fn frame_time(&self, ctx: &mut CheatCtx) -> Result<f32, Error> {
Ok(ctx.process.read(self.0 + 0x8)?)
}
pub fn absolute_frame_time(&self, ctx: &mut CheatCtx) -> Result<f32, Error> {
Ok(ctx.process.read(self.0 + 0xC)?)
}
pub fn max_clients(&self, ctx: &mut CheatCtx) -> Result<i32, Error> {
Ok(ctx.process.read(self.0 + 0x10)?)
}
pub fn tick_count(&self, ctx: &mut CheatCtx) -> Result<i32, Error> {
Ok(ctx.process.read(self.0 + 0x40)?)
}
pub fn map_name(&self, ctx: &mut CheatCtx) -> Result<String, Error> {
let ptr = ctx.process.read_addr64(self.0 + 0x188)?;
Ok(ctx.process.read_char_string_n(ptr, 32)?)
}
}
/*
struct GlobalVarsBase {
real_time: f32, // 0x0000
frame_count: i32, // 0x0004
frame_time: f32, // 0x0008
absolute_frame_time: f32, // 0x000C
max_clients: i32, // 0x0010
pad_0: [u8; 0x14], // 0x0014
frame_time_2: f32, // 0x0028
current_time: f32, // 0x002C
current_time_2: f32, // 0x0030
pad_1: [u8; 0xC], // 0x0034
tick_count: f32, // 0x0040 // NO fucking idea why the fuck this "should" be an f32????
pad_2: [u8; 0x4], // 0x0044
network_channel: *const c_void, // 0x0048
pad_3: [u8; 0x130], // 0x0050
current_map: *const c_char, // 0x0180
current_map_name: *const c_char, // 0x0188
}
*/

View File

@@ -1,5 +1,9 @@
mod vec3;
mod global_vars;
mod gamerules;
mod entity;
pub use vec3::Vec3;
pub use global_vars::GlobalVars;
pub use gamerules::GameRules;
pub use entity::{CBaseEntity, CPlayerController, CPlayerPawn};