A feature is a self-contained gameplay tweak (GodMode, InfiniteJetpack,
SpinBot, ...) driven once per rendered frame from the DirectX 11 present hook
(PostRender). The framework lives in
Internal/features/.
| File | Role |
|---|---|
Feature.h |
Abstract base class every feature derives from. |
FeatureRunner.h |
The registry (Features::Features) and the per-frame loop (Features::Execute). Depends only on Feature.h, so it is unit-testable. |
Features.h |
Includes every concrete feature and registers them in Features::Init(). |
*.h (the rest) |
The concrete features. |
class Feature {
public:
bool Enabled = false; // toggled from settings via UpdateEnabled()
bool Initialized = false;
bool OneTime = false; // Run() fires once per enable instead of every frame
std::string Name = "BaseFeature";
// applied / hasRun are framework bookkeeping — features must not touch them.
virtual void Init() = 0;
virtual void UpdateEnabled() = 0;
virtual bool Check() = 0;
virtual void Destroy() = 0;
virtual void Run() = 0;
void Log(std::string message); // gated by Settings.DEBUG.FeaturesLogging
};Each frame, Features::Execute() runs, for every registered feature:
if (!Initialized) Init();
UpdateEnabled(); // refresh Enabled from settings
if (!Check()) continue; // Check() == "is it valid/safe to act right now?"
if (Enabled) Run(); // once, if OneTime
else if (applied) Destroy(); // revert, once, on the enabled -> disabled edge
Implement the virtuals as:
Init()— one-time setup (cache pointers/originals); setInitialized.UpdateEnabled()—Enabled = Settings.<section>.<flag>;. Called reactively onEvents::Type::SettingsChanged(dispatched by the menu), not every frame.Check()— return whether it is currently valid to act (pointers ready, in a match, ...). Do not gate this onEnabledand do not returnEnabled— the loop decides Run vs Destroy.Run()— apply the effect. Called every frame while enabled, or once per enable ifOneTime.Destroy()— revert whateverRun()applied. Called once, on the enabled → disabled edge.
Historically every feature's Check() ended with return Enabled. That made a
disabled feature return false, so the loop skipped it and Destroy() never
ran — disabling a feature never reverted its effect. Returning validity
instead fixes that and lets the loop revert exactly once on disable.
Features that still return Enabled keep working (they simply never reach
Destroy()), so migration can be incremental.
Set OneTime = true for a feature whose Run() should fire once per enable
rather than every frame. It re-arms when disabled.
Every feature has Triggers — a std::vector<Events::Type>, default
{Render}. Render triggers run from the per-frame Features::Execute loop.
Every other trigger is subscribed to the event bus in
Features::Init and driven by Features::RunFeature when that event is
dispatched. A feature may list several triggers (and may mix Render with
bus events) — e.g. DiscordPresence sets
Triggers = {Events::Type::EnteredGame, Events::Type::EnteredLobby} to refresh
only when the in-game/in-lobby state flips. Same toggle/Check/Run/Destroy
contract, just a different clock.
The triggering event and its Events::Payload are forwarded to Run(). Override
whichever arity you need: Run() to ignore the trigger (the common case),
Run(Events::Type event) for the event only, or
Run(Events::Type event, const Events::Payload& payload) for both — the base
delegates down the chain, so existing nullary Run() overrides keep working.
One-shot actions that don't need a toggle (like the "Load into map" button)
are better as a plain event handler than a feature — see the LoadIntoMap
handler registered in Features::Init, dispatched by the button via
Events::Dispatch(Events::Type::LoadIntoMap, payload) with payload.name
carrying the level chosen in the Misc tab's dropdown.
- Create
Internal/features/MyFeature.hderiving fromFeature. #include "MyFeature.h"inFeatures.hand addFeatures.push_back(std::make_unique<MyFeature>());toFeatures::Init().- Add the header to
Internal.vcxproj/.filters(or let Visual Studio do it).
GodMode, InfiniteJetpack, NoRecoil, SpinBot, PlayerModifications, WeaponModifications, ThirdPerson, FreeCam, Radar, Esp (player name/box/bones/snaplines/health/distance ESP, configured in the Visuals tab), UserScripts (runs Python user scripts — see scripting.md).
The framework (init/check/run/destroy transitions, OneTime, legacy contract) is
covered by Tests/FeaturesTests.cpp using fake features — see
testing.md. The concrete features touch the live game and are not
unit tested.