Game cheat base and clean architecture for your next cheat
- Clean architecture
- Force your code to be maintainable and easy to read
- Easy to use, initialize and unload(dll unload)
- Easy global/shared data access
- Logger (Basic one)
- Hooks:
- Un/Detour
- Un/VMT swap
- Memory:
- Pattern scan
- Value scan (
TODO
)
Options presented by CleanCheatOptions
struct that are passed when initialize CleanCheat
To use CleanCheat
you need to do 3 simple steps
At first your project need to know about CleanCheat
. For that you have to:
- Copy
src/CleanCheat
andsrc/CleanCheat.h
OR download Latest Release to your project dir - Add
#include "CleanCheat.h"
into your cpp/h files (if you have precompiled headers it is a good place to add this include there)
Note:
In cpp/h files you need to include CleanCheat.h
NOT CleanCheat/CleanCheat.h
You need to pick your options and pass it to CleanCheat::Init
function
CleanCheatOptions options;
options.UseLogger = true;
CleanCheat::Init(options);
Features used in the example (BasicFeature, TestFeature)
int initData = 1;
BasicFeature basic;
basic.Init(&initData);
TestFeature test;
test.Init();
Features must to be initialized before registers it in any runner BasicRunner
BasicRunner basicRunner;
basicRunner.RegisterFeature(&basic);
basicRunner.RegisterFeature(&test);
// Register the runner
CleanCheat::RegisterRunner(&basicRunner);
You need to include CleanCheat.h
where ever you want to access CleanCheat
then you can access what Features it provide,
for sure that's after Initialize CleanCheat
CleanCheat
do all it's operation from CleanCheat::Tick
so it needs to be called from any place that get called at least once per frame:
MessageHandler
while (!(GetAsyncKeyState(VK_END) & 1))
{
CleanCheat::Tick(&initData);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
CleanCheat::Discard();
Frame hook:
InternalUnrealExample use PostRender
hooked function to do it.
You can hook any (DirectX, OpenGL, Vulkan, etc) that called every frame
You can access shared data related stuff using CleanCheat::SharedData
CleanCheat::SharedData->ANY_DATA;
You can log by LOG
macro that's only work if UseLogger option are ture
int main(int argc, char* argv[])
{
// Init CleanCheat
...
// Log
LOG("Hello '%s' users", "CleanCheat");
return 0;
}
Output:
Under the hood it use std::printf
function with format [FILE_NAME:FUNC_NAME:CODE_LINE] USER_MESSAGE
[Main.cpp:main:11] Hello 'CleanCheat' users
You can access hooking related stuff using CleanCheat::Hook
void* outOrignalMethodAddress = nullptr;
CleanCheat::Hook->SwapVmt(INSTANCE_ADDRESS, METHOD_INDEX, &HOOK_FUNC, &outOrignalMethodAddress);
void* functionAddress = 0x123456CD;
CleanCheat::Hook->Detour(&functionAddress, &HOOK_FUNC);
You can access memory related stuff using CleanCheat::Memory
// Look for "48 89 5C 24" in main module, and maximum result 2
std::vector<void*> addrs = CleanCheat::Memory->PatternScan("48 89 5C 24", 2);
The place where you would store global status and shared data, you could also add functions too. As every code need it's own collection of shared data you will need to make your own class.
- Make a class that inherits from
SharedDataBase
- Include its header in
CleanCheat.h
next to// Your SharedData class
- Edit
SHARED_DATA_TYPE
inCleanCheat/Macros.h
with your shared data class name (default isSharedDataStruct
)
Then you can use shared data
Let's say i have level objects loop that have like 10k object, and every type of objects have different task to do,
if i have PlayerObject
and WeaponObject
, and i want to make ESP for players and zero recoil for weapons,
then that is a different tasks but related to same task(object iterate).
Problem example (trust me, code will be (very UGLY and very hard to maintain) fast):
for (int i = 0; i < OBJECT_COUNT; i)
{
if (OBJECT[i] is PlayerObject) // Condetion
{
if (!OBJECT[i]->IsDead() && OBJECT[i]->Distance < 100.f) // Condetion
{
// Collect/Preper data
...
// Draw
...
}
}
if (OBJECT[i] is WeaponObject) // Condetion
{
if (!OBJECT[i]->IsEmpty()) // Condetion
{
// Collect/Preper data
...
// Handle
OBJECT[i]->Recoil = 0.f;
}
}
}
Runner concept present task that can be splited into tasks(Features) with providing input to that tasks to check and handle.
In our example runner will present the loop for (int i = 0; i < OBJECT_COUNT; i)
and iterator Object
as input for our Features.
bool LevelObjectsRunner::Condition()
{
return CleanCheat::SharedData && CleanCheat::SharedData->World;
}
void LevelObjectsRunner::OnExecute()
{
for (int32_t i = 0; i < curLevel->Objects.Count(); i )
{
auto* curObject = curLevel->Objects[i];
if (!curObject)
continue;
// Here we execute all registered features with 'curObject' as input
ExecuteFeatures(curObject);
}
}
Func | Description |
---|---|
OnExecute | Called by CleanCheat every tick |
Condition | Called before OnExecute by CleanCheat to determine run it or not |
Discard | Called by CleanCheat when CleanCheat itself get discarded |
Feature concept present runner sub-task that can do one task with one input after pass a condition. So you can't pass same feature to multi runner (unexpected behavior). Feature are there to spilt runner code and make it easy to maintain easy to read.
In our example feature can be presented as a Player ESP task and Weapon no recoil task in separate isolated class like that:
bool EspFeature::Condition(Object* curObject)
{
return curObject is PlayerObject && !curObject->IsDead() && curObject->Distance < 100.f;
}
void EspFeature::OnExecute(Object* curObject)
{
// Collect/Preper data
...
// Draw
...
}
bool WeaponZeroRecoilFeature::Condition(Object* curObject)
{
return curObject is WeaponObject && !curObject->IsEmpty();
}
void WeaponZeroRecoilFeature::OnExecute(Object* curObject)
{
// Collect/Preper data
...
// Handle
curObject->Recoil = 0.f;
}
Func | Description |
---|---|
OnInit | Called only once by user before it get registers by runner |
OnExecute | Called by runner during runner executive once or more depend on the runner |
Condition | Called before OnExecute by runner to determine whether it will call it or not |
BeforeExecute | Called by runner before call OnExecute always get called, doesn't care about Condition |
AfterExecute | Called by runner after call OnExecute always get called, doesn't care about Condition |
Discard | Called by runner when runner itself get discarded |
There are a number of examples that demonstrate various aspects of using CleanCheat
. They can be found in the examples folder:
Example | Description |
---|---|
Simple | Simple example |
InternalUnrealEngine | Shows how to use with CheatGear |
- Add feature settings
- First release