Sign in Sign up
kretrod/lodestone-cpp Public
Branches
main
572 lines (500 loc) · 19.4 KB Raw
// Everything the menu owns: what is switched on, what the last frame saw, and
// the keys bound to it.
//
// The shape mirrors the Rust client deliberately, so the saved config file is
// the same format and a config written by either build means the same thing.
//
// One design note worth keeping: modules are addressed through a table rather
// than a giant switch. Each entry knows its label, its config key, and how to
// reach its bool, which is what lets the menu, the keybinds and the config file
// all iterate the same list instead of repeating it three times.
#pragma once

#include <windows.h>

#include <array>
#include <cstdint>
#include <string>
#include <vector>

namespace lodestone::state {

// ---------------------------------------------------------------------------
// modes
// ---------------------------------------------------------------------------

enum class AimMode { Camera, Silent };
enum class FlyMode { Vanilla, Smooth, Glide, Teleport, Creative };
enum class SpeedMode { Vanilla, Strafe, Bhop };
enum class AuraTarget { Nearest, Weakest, Angle };
enum class BoxStyle { Full, Corners, Filled };
enum class TotemMode { Legit, Instant };

inline const char* label_of(AimMode m) {
    switch (m) {
        case AimMode::Camera: return "Camera";
        case AimMode::Silent: return "Silent";
    }
    return "?";
}
inline const char* label_of(TotemMode m) {
    switch (m) {
        case TotemMode::Legit: return "Legit";
        case TotemMode::Instant: return "Instant";
    }
    return "?";
}
inline const char* label_of(FlyMode m) {
    switch (m) {
        case FlyMode::Vanilla: return "Vanilla";
        case FlyMode::Smooth: return "Smooth";
        case FlyMode::Glide: return "Glide";
        case FlyMode::Teleport: return "Teleport";
        case FlyMode::Creative: return "Creative";
    }
    return "?";
}
inline const char* label_of(SpeedMode m) {
    switch (m) {
        case SpeedMode::Vanilla: return "Vanilla";
        case SpeedMode::Strafe: return "Strafe";
        case SpeedMode::Bhop: return "Bhop";
    }
    return "?";
}
inline const char* label_of(AuraTarget m) {
    switch (m) {
        case AuraTarget::Nearest: return "Nearest";
        case AuraTarget::Weakest: return "Weakest";
        case AuraTarget::Angle: return "Angle";
    }
    return "?";
}
inline const char* label_of(BoxStyle m) {
    switch (m) {
        case BoxStyle::Full: return "Full";
        case BoxStyle::Corners: return "Corners";
        case BoxStyle::Filled: return "Filled";
    }
    return "?";
}

using Colour = std::array<float, 4>;

// ---------------------------------------------------------------------------
// config
// ---------------------------------------------------------------------------

struct Combat {
    bool kill_aura = false;
    AuraTarget aura_target = AuraTarget::Nearest;
    float aura_range = 3.5f;
    float aura_cps = 10.0f;
    bool aura_cooldown = true;
    bool aura_rotate = true;
    bool aura_through_walls = false;
    bool aura_players = true;
    bool aura_mobs = true;
    bool aura_animals = false;

    bool aimbot = false;
    AimMode aim_mode = AimMode::Silent;
    float aim_fov = 60.0f;
    float aim_speed = 0.35f;

    bool trigger_bot = false;
    float trigger_delay = 0.05f;

    bool auto_dodge = false;
    float dodge_range = 8.0f;
    float dodge_speed = 0.25f;
    bool dodge_arrows = true;
    bool dodge_cliffs = true;

    bool reach = false;
    float reach_distance = 3.5f;

    bool auto_clicker = false;
    float click_cps = 12.0f;
    float click_jitter = 0.3f;

    bool criticals = false;
    float crit_hop = 0.2f;
    bool sprint_reset = false;

    bool hitbox = false;
    float hitbox_expand = 0.2f;

    bool bow_aimbot = false;
    bool backtrack = false;
    float backtrack_ms = 120.0f;

    bool auto_totem = false;
    /// How eagerly the totem goes across. Inventory timing is one of the
    /// cheapest things for a server to check, and a totem that appears the same
    /// millisecond every hit lands is the clearest tell there is — so Legit
    /// waits until the health is genuinely dangerous and takes a human beat
    /// about it, while Instant goes as fast as the container click can.
    TotemMode totem_mode = TotemMode::Instant;
    bool auto_shield = false;

    /// Breaks the end crystals near you — the half of crystal PvP that deals
    /// the damage. Placing needs a face to build on beside the target, which is
    /// a separate problem.
    bool auto_crystal = false;
    float crystal_range = 5.0f;
    float crystal_delay = 0.1f;
    /// The mace's damage scales with how far you have fallen, so it is worth
    /// nothing on the ground and everything on the way down.
    bool auto_mace = false;

    bool anti_knockback = false;
    float kb_horizontal = 0.0f;
    float kb_vertical = 0.0f;
};

struct Movement {
    bool fly = false;
    FlyMode fly_mode = FlyMode::Smooth;
    float fly_speed = 0.4f;
    float fly_step = 2.0f;
    float fly_step_interval = 0.25f;
    bool fly_anti_kick = true;
    float fly_dip_interval = 1.5f;

    bool speed = false;
    SpeedMode speed_mode = SpeedMode::Strafe;
    float speed_value = 0.3f;

    bool bhop = false;
    bool sprint = false;
    bool no_fall = false;
    bool noclip = false;
    bool jesus = false;

    bool spider = false;
    float spider_power = 0.3f;

    bool step = false;
    float step_height = 1.2f;

    bool jump_power = false;
    float jump_multiplier = 1.5f;

    bool jetpack = false;
    float jetpack_power = 0.5f;

    bool freecam = false;
    float freecam_speed = 1.0f;

    bool blink = false;
    bool safewalk = false;

    bool timer = false;
    float timer_speed = 1.0f;

    /// Keep within what the server will re-simulate without snapping you back.
    bool speed_limit = true;
};

struct Esp {
    bool players = false;
    bool mobs = false;
    bool animals = false;
    bool items = false;
    bool containers = false;

    bool xray = false;
    float block_radius = 24.0f;
    std::vector<bool> xray_selected;

    bool base_finder = false;
    float base_chunk_radius = 8.0f;

    bool gizmo_esp = false;
    bool show_invis = true;
    bool show_ping = true;
    bool show_threat = true;
    bool show_gear = true;
    bool server_ghost = false;
    bool player_radar = false;

    bool boxes = true;
    bool tracers = false;
    bool nametags = true;
    bool health_bars = true;
    float distance = 96.0f;

    BoxStyle box_style = BoxStyle::Full;
    bool box_fill = false;
    Colour color_player{0.94f, 0.38f, 0.43f, 1.0f};
    Colour color_mob{0.93f, 0.63f, 0.33f, 1.0f};
    Colour color_animal{0.49f, 0.85f, 0.55f, 1.0f};
    Colour color_item{0.47f, 0.75f, 0.93f, 1.0f};
    Colour color_threat{1.0f, 0.33f, 0.33f, 1.0f};
    Colour color_invis{0.75f, 0.55f, 1.0f, 1.0f};
};

struct Building {
    bool no_cooldown = false;
    bool fast_place = false;
    bool block_reach = false;
    float block_reach_dist = 4.5f;
    bool air_place = false;
    float place_delay = 0.05f;
    bool auto_build = false;
};

struct Visuals {
    bool fullbright = false;
    bool no_culling = false;
    bool view_distance = false;
    float view_distance_chunks = 16.0f;
    bool fast_chunks = false;
    float fast_chunks_rate = 60.0f;
    bool no_fog = false;
    bool no_weather = false;
    bool no_hurt_cam = false;
    bool no_bob = false;
    bool fov = false;
    float fov_value = 90.0f;
    bool watermark = false;
    bool hud_coords = false;
    bool hud_modules = false;
};

struct Misc {
    bool auto_respawn = false;
    bool hide_from_capture = false;
    bool packet_log = false;
};

struct Config {
    Combat combat;
    Movement movement;
    Esp esp;
    Building building;
    Visuals visuals;
    Misc misc;
    float ui_scale = 1.0f;
    /// The one accent the whole UI is themed around — #B7CDD4 by default.
    Colour accent{0.718f, 0.804f, 0.831f, 1.0f};
    /// Win32 virtual-key that opens the menu (Insert), rebindable.
    uint32_t menu_key = 0x2D;
};

// ---------------------------------------------------------------------------
// the module table
// ---------------------------------------------------------------------------

/// Reaching a module's flag. A function rather than a member pointer because
/// the flags live in different nested structs.
using Flag = bool& (*)(Config&);

struct Module {
    const char* category;  ///< which nav pane it lives under
    const char* label;     ///< shown in the menu
    const char* key;       ///< stable name in the config file
    Flag flag;
};

/// The nav rail, in order. Kept beside the table so the two cannot drift.
inline const std::vector<const char*>& categories() {
    static const std::vector<const char*> all = {"Combat",  "Movement", "World",   "ESP",
                                                 "Visuals", "Misc",     "Settings"};
    return all;
}

#define LODE_FLAG(path) \
    +[](Config& c) -> bool& { return c.path; }

/// Every toggle, in menu order. The menu, the keybinds and the config file all
/// walk this one list.
inline const std::vector<Module>& modules() {
    static const std::vector<Module> table = {
        // combat
        {"Combat", "Kill Aura", "kill_aura", LODE_FLAG(combat.kill_aura)},
        {"Combat", "Aimbot", "aimbot", LODE_FLAG(combat.aimbot)},
        {"Combat", "Trigger Bot", "trigger_bot", LODE_FLAG(combat.trigger_bot)},
        {"Combat", "Auto Dodge", "auto_dodge", LODE_FLAG(combat.auto_dodge)},
        {"Combat", "Reach", "reach", LODE_FLAG(combat.reach)},
        {"Combat", "Auto Clicker", "auto_clicker", LODE_FLAG(combat.auto_clicker)},
        {"Combat", "Criticals", "criticals", LODE_FLAG(combat.criticals)},
        {"Combat", "Sprint Reset", "sprint_reset", LODE_FLAG(combat.sprint_reset)},
        {"Combat", "Hitbox", "hitbox", LODE_FLAG(combat.hitbox)},
        {"Combat", "Bow Aimbot", "bow_aimbot", LODE_FLAG(combat.bow_aimbot)},
        {"Combat", "Backtrack", "backtrack", LODE_FLAG(combat.backtrack)},
        {"Combat", "Auto Totem", "auto_totem", LODE_FLAG(combat.auto_totem)},
        {"Combat", "Auto Crystal", "auto_crystal", LODE_FLAG(combat.auto_crystal)},
        {"Combat", "Auto Mace", "auto_mace", LODE_FLAG(combat.auto_mace)},
        {"Combat", "Auto Shield", "auto_shield", LODE_FLAG(combat.auto_shield)},
        {"Combat", "Anti Knockback", "anti_knockback", LODE_FLAG(combat.anti_knockback)},
        // movement
        {"Movement", "Fly", "fly", LODE_FLAG(movement.fly)},
        {"Movement", "Speed", "speed", LODE_FLAG(movement.speed)},
        {"Movement", "Bhop", "bhop", LODE_FLAG(movement.bhop)},
        {"Movement", "Auto Sprint", "sprint", LODE_FLAG(movement.sprint)},
        {"Movement", "No Fall", "no_fall", LODE_FLAG(movement.no_fall)},
        {"Movement", "Noclip", "noclip", LODE_FLAG(movement.noclip)},
        {"Movement", "Jesus", "jesus", LODE_FLAG(movement.jesus)},
        {"Movement", "Spider", "spider", LODE_FLAG(movement.spider)},
        {"Movement", "Step", "step", LODE_FLAG(movement.step)},
        {"Movement", "High Jump", "jump_power", LODE_FLAG(movement.jump_power)},
        {"Movement", "Jetpack", "jetpack", LODE_FLAG(movement.jetpack)},
        {"Movement", "Freecam", "freecam", LODE_FLAG(movement.freecam)},
        {"Movement", "Blink", "blink", LODE_FLAG(movement.blink)},
        {"Movement", "Safewalk", "safewalk", LODE_FLAG(movement.safewalk)},
        {"Movement", "Timer", "timer", LODE_FLAG(movement.timer)},
        // world
        {"World", "No Cooldown", "no_cooldown", LODE_FLAG(building.no_cooldown)},
        {"World", "Fast Place", "fast_place", LODE_FLAG(building.fast_place)},
        {"World", "Block Reach", "block_reach", LODE_FLAG(building.block_reach)},
        {"World", "Air Place", "air_place", LODE_FLAG(building.air_place)},
        {"World", "Auto Build", "auto_build", LODE_FLAG(building.auto_build)},
        // esp
        {"ESP", "Players", "esp_players", LODE_FLAG(esp.players)},
        {"ESP", "Mobs", "esp_mobs", LODE_FLAG(esp.mobs)},
        {"ESP", "Animals", "esp_animals", LODE_FLAG(esp.animals)},
        {"ESP", "Items", "esp_items", LODE_FLAG(esp.items)},
        {"ESP", "Containers", "esp_containers", LODE_FLAG(esp.containers)},
        {"ESP", "Base Finder", "base_finder", LODE_FLAG(esp.base_finder)},
        {"ESP", "X-Ray", "xray", LODE_FLAG(esp.xray)},
        {"ESP", "Player Radar", "player_radar", LODE_FLAG(esp.player_radar)},
        {"ESP", "3D Boxes", "gizmo_esp", LODE_FLAG(esp.gizmo_esp)},
        {"ESP", "Boxes", "esp_boxes", LODE_FLAG(esp.boxes)},
        {"ESP", "Tracers", "esp_tracers", LODE_FLAG(esp.tracers)},
        {"ESP", "Nametags", "esp_nametags", LODE_FLAG(esp.nametags)},
        {"ESP", "Health Bars", "esp_health", LODE_FLAG(esp.health_bars)},
        {"ESP", "Show Invisible", "show_invis", LODE_FLAG(esp.show_invis)},
        {"ESP", "Show Ping", "show_ping", LODE_FLAG(esp.show_ping)},
        {"ESP", "Show Threat", "show_threat", LODE_FLAG(esp.show_threat)},
        {"ESP", "Show Gear", "show_gear", LODE_FLAG(esp.show_gear)},
        {"ESP", "Server Ghost", "server_ghost", LODE_FLAG(esp.server_ghost)},
        // visuals
        {"Visuals", "Fullbright", "fullbright", LODE_FLAG(visuals.fullbright)},
        {"Visuals", "No Culling", "no_culling", LODE_FLAG(visuals.no_culling)},
        {"Visuals", "Extend View", "view_distance", LODE_FLAG(visuals.view_distance)},
        {"Visuals", "Fast Chunks", "fast_chunks", LODE_FLAG(visuals.fast_chunks)},
        {"Visuals", "No Fog", "no_fog", LODE_FLAG(visuals.no_fog)},
        {"Visuals", "No Weather", "no_weather", LODE_FLAG(visuals.no_weather)},
        {"Visuals", "No Hurt Cam", "no_hurt_cam", LODE_FLAG(visuals.no_hurt_cam)},
        {"Visuals", "No Bob", "no_bob", LODE_FLAG(visuals.no_bob)},
        {"Visuals", "Custom FOV", "fov", LODE_FLAG(visuals.fov)},
        {"Visuals", "Watermark", "watermark", LODE_FLAG(visuals.watermark)},
        {"Visuals", "HUD Coords", "hud_coords", LODE_FLAG(visuals.hud_coords)},
        {"Visuals", "HUD Modules", "hud_modules", LODE_FLAG(visuals.hud_modules)},
        // misc
        {"Misc", "Auto Respawn", "auto_respawn", LODE_FLAG(misc.auto_respawn)},
        {"Misc", "Hide From Capture", "hide_capture", LODE_FLAG(misc.hide_from_capture)},
        {"Misc", "Packet Log", "packet_log", LODE_FLAG(misc.packet_log)},
    };
    return table;
}

#undef LODE_FLAG

// ---------------------------------------------------------------------------
// what the last frame saw
// ---------------------------------------------------------------------------

enum class TargetKind { Player, Mob, Animal, Item, Other };

struct Vec3 {
    double x = 0, y = 0, z = 0;
};

struct Target {
    Vec3 min, max;
    TargetKind kind = TargetKind::Other;
    std::string name;
    std::string held;
    std::vector<std::string> effects;
    int armor = 0;
    int ping = -1;
    float health = 0.0f;
    float max_health = 0.0f;
    float distance = 0.0f;
    bool invisible = false;
    /// Its own reach already covers you — the one thing worth seeing first.
    bool can_reach_you = false;
};

struct GameState {
    bool in_world = false;
    bool single_player = false;
    Vec3 pos, eye, camera;
    float yaw = 0, pitch = 0;
    float camera_yaw = 0, camera_pitch = 0;
    float fov = 70.0f;
    bool on_ground = false;
    bool sprinting = false;
    bool screen_open = false;
    float health = 0.0f;
    /// Counts down from 10 after damage lands, so a rise means a hit just
    /// arrived. That edge is what the totem and the shield react to — far
    /// earlier and more reliably than watching health alone.
    int hurt_time = 0;
    float fps = 0.0f;
    int loaded_chunks = 0;

    std::vector<Target> targets;
    std::string server_brand;

    /// The local player's skin: the GL texture the game already has resident,
    /// and whether the model is the slim variant. 0 means "not resolved".
    uint32_t skin_texture = 0;
    bool skin_slim = false;
    std::string player_name;
};

// ---------------------------------------------------------------------------
// keybinds and the shared state
// ---------------------------------------------------------------------------

enum class BindKind { Module, Panic, MenuToggle };

struct BindTarget {
    BindKind kind = BindKind::Module;
    /// Index into `modules()` when kind is Module.
    size_t module = 0;

    bool operator==(const BindTarget&) const = default;
};

struct Bind {
    uint32_t key = 0;
    BindTarget target;
};

/// Framework-neutral input, translated from Win32 by the window hook and
/// drained by the menu each frame.
struct UiInput {
    enum class Kind { MouseMove, MouseButton, Wheel, Key, Char } kind{};
    float x = 0, y = 0;
    uint8_t button = 0;
    bool down = false;
    uint32_t vk = 0;
    unsigned int codepoint = 0;
};

struct Shared {
    bool menu_open = false;
    bool eject = false;
    Config cfg;
    GameState game;
    std::vector<UiInput> events;
    std::vector<Bind> binds;
    std::vector<std::string> missing;
    std::string status;
    /// Set while waiting for the next key press to bind something.
    bool binding_active = false;
    BindTarget binding;

    /// Turn everything off at once.
    void panic_off() {
        for (const auto& m : modules()) m.flag(cfg) = false;
    }

    /// A key press. Returns true if it was ours and the game should not see it.
    bool handle_key(uint32_t vk) {
        if (binding_active) {
            binding_active = false;
            // Escape clears a bind rather than setting one.
            if (vk == 0x1B) return true;
            if (binding.kind == BindKind::MenuToggle) {
                cfg.menu_key = vk;
                return true;
            }
            std::erase_if(binds, [&](const Bind& b) { return b.key == vk || b.target == binding; });
            binds.push_back(Bind{vk, binding});
            return true;
        }
        for (const auto& b : binds) {
            if (b.key != vk) continue;
            switch (b.target.kind) {
                case BindKind::Module: {
                    bool& f = modules()[b.target.module].flag(cfg);
                    f = !f;
                    break;
                }
                case BindKind::Panic: panic_off(); break;
                case BindKind::MenuToggle: break;  // lives in cfg.menu_key
            }
            return true;
        }
        return false;
    }
};

/// A lock built on a critical section rather than std::mutex.
///
/// The mingw toolchain here is the win32-threads build, whose libstdc++ ships
/// without <mutex>, <thread> and friends. That is no loss: a critical section
/// is exactly what this wants, and going the other way — the posix-threads
/// toolchain — would drag libwinpthread into a DLL that currently needs nothing
/// but the system libraries.
class Lock {
  public:
    Lock() { InitializeCriticalSection(&cs_); }
    ~Lock() { DeleteCriticalSection(&cs_); }
    Lock(const Lock&) = delete;
    Lock& operator=(const Lock&) = delete;

    void enter() { EnterCriticalSection(&cs_); }
    void leave() { LeaveCriticalSection(&cs_); }

  private:
    CRITICAL_SECTION cs_{};
};

class Guard {
  public:
    explicit Guard(Lock& l) : lock_(l) { lock_.enter(); }
    ~Guard() { lock_.leave(); }
    Guard(const Guard&) = delete;
    Guard& operator=(const Guard&) = delete;

  private:
    Lock& lock_;
};

/// The one instance, and the lock around it. The render thread writes it and
/// the window procedure reads it, so every touch goes through `with`.
inline Lock g_lock;
inline Shared g_shared;

template <typename F>
inline auto with(F&& f) -> decltype(f(g_shared)) {
    Guard guard(g_lock);
    return f(g_shared);
}

}  // namespace lodestone::state