- Status
- Offline
- Joined
- Apr 18, 2019
- Messages
- 147
- Reaction score
- 249
Technique to efficiently scan the game's entity list. It is based on diffing the ent_info array to see what needs to be updated. Very useful code that increase optimization in cheats.
This is just a demo code required for detecting when an entity slot has changed, and only updating those entities you care about when necessary.
Credits: Casual_Hacker
This is just a demo code required for detecting when an entity slot has changed, and only updating those entities you care about when necessary.
Code:
#include <string>
#include <cstdint>
#include <vector>
// put in here apex sdk
namespace sdk {
const size_t NUM_ENT_ENTRIES = 0x10000;
struct CEntInfo {
uint64_t pEntity;
int64_t SerialNumber;
uint64_t pPrev;
uint64_t pNext;
};
}
// Your process class to access Apex Legends
class Process {
public:
Process();
bool heartbeat();
// Reads an array of `T`.
template<typename T>
bool read_array(uint64_t address, T* ptr, size_t len);
// Reads a value of `T`.
template<typename T>
bool read(uint64_t address, T& value);
public:
uint64_t r5apex_exe;
};
// Your process local entity hierarchy
namespace entities {
class BaseEntity {
public:
virtual ~BaseEntity() = 0;
// Read from the process to update this entity's state
virtual void update(Process& process) = 0;
};
class Player : public BaseEntity {
public:
Player(uint64_t entity_ptr) : entity_ptr(entity_ptr) {}
virtual void update(Process& process);
public:
uint64_t entity_ptr;
float origin[3];
float angles[3];
int team_index;
int shield_health;
int max_shield_health;
int health;
int max_health;
float view_offset[3];
// etc...
};
class PropSurvival : public BaseEntity {
public:
PropSurvival(uint64_t entity_ptr) : entity_ptr(entity_ptr) {}
virtual void update(Process& process);
public:
uint64_t entity_ptr;
float origin[3];
int custom_script_int;
};
class WeaponX : public BaseEntity {
public:
WeaponX(uint64_t entity_ptr) : entity_ptr(entity_ptr) {}
virtual void update(Process& process);
public:
uint64_t entity_ptr;
int weapon_owner;
int weapon_name_index;
int ammo_in_clip;
int ammo_in_stockpile;
};
}
//----------------------------------------------------------------
// Your hack
std::string get_client_class_name(Process& process, uint64_t entity_ptr);
int main() {
// Your process management (eg through driver)
Process process;
// Offset of entity list
uint32_t cl_entitylist = 0x01f9ae68;
// Cached state of process local entity data
std::vector<entities::BaseEntity*> entities;
std::vector<sdk::CEntInfo> ent_infos;
std::vector<sdk::CEntInfo> prev_infos;
// Initialize the cached state
for (size_t i = 0; i < sdk::NUM_ENT_ENTRIES; ++i) {
entities.push_back(nullptr);
ent_infos.push_back(sdk::CEntInfo{});
prev_infos.push_back(sdk::CEntInfo{});
}
// While the process is running
while (process.heartbeat()) {
// Keep the ent_infos around to diff for changes
std::swap(ent_infos, prev_infos);
// Read the game's current ent_infos array
process.read_array(process.r5apex_exe + cl_entitylist, ent_infos.data(), sdk::NUM_ENT_ENTRIES);
// Update the entities
for (size_t i = 0; i < sdk::NUM_ENT_ENTRIES; ++i) {
auto& entity_place = entities[i];
auto& prev_info = prev_infos[i];
auto& ent_info = ent_infos[i];
// If the entity pointer remained the same
if (prev_info.pEntity == ent_info.pEntity) {
// Update its state
if (entity_place) {
entity_place->update(process);
}
}
// The entity pointer changed to something non-null
else if (ent_info.pEntity != 0) {
delete entity_place;
entity_place = nullptr;
auto class_name = get_client_class_name(process, ent_info.pEntity);
if (class_name == "CPlayer") {
entity_place = new entities::Player(ent_info.pEntity);
}
else if (class_name == "CPropSurvival") {
entity_place = new entities::PropSurvival(ent_info.pEntity);
}
else if (class_name == "CWeaponX") {
entity_place = new entities::WeaponX(ent_info.pEntity);
}
if (entity_place) {
entity_place->update(process);
}
}
// The entity pointer became null do cleanup
else {
delete entity_place;
entity_place = nullptr;
}
}
}
}
std::string get_client_class_name(Process& process, uint64_t entity_ptr) {
uint64_t client_networkable_vtable;
uint64_t get_client_entity;
uint32_t offset;
uint64_t network_name_ptr;
char buffer[32];
// Read the ClientClass's network name for to given entity
bool success = process.read(entity_ptr + 3 * 8, client_networkable_vtable)
&& process.read(client_networkable_vtable + 3 * 8, get_client_entity)
&& process.read(get_client_entity + 3, offset)
&& process.read(get_client_entity + offset + 7 + 16, network_name_ptr)
&& process.read_array(network_name_ptr, buffer, 32);
std::string result;
if (success) {
// Return up to 32 chars from the network name
size_t len;
for (len = 0; len < 32; ++len)
if (buffer[len] == '\0')
break;
result.assign(buffer, len);
}
return result;
}
Credits: Casual_Hacker