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

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 {}