Sign in Sign up
kretrod/lodestone Public
Branches
master
705 lines (643 loc) · 22.1 KB Raw
//! Shared state between the window procedure and the render hook.

use std::sync::{Mutex, OnceLock};

/// How flight moves you. The differences matter: a server watches the position
/// updates your client sends, and the further those are from something a
/// vanilla client could produce, the more obvious they are.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum FlyMode {
    /// Flip the client's own creative-flight ability. Simple, and exactly what
    /// a legitimately-creative player looks like — to a server that granted
    /// flight. To one that did not, the ability is not the tell; the position
    /// stream is.
    Creative,
    /// Drive velocity directly, accelerating and decelerating, so the position
    /// stream stays smooth and continuous.
    Motion,
    /// No vertical acceleration: hover, with gravity cancelled each tick.
    Glide,
    /// Jump forward in steps. Fast, and the least like anything vanilla —
    /// every step is a discontinuity in the position stream.
    Teleport,
}

impl FlyMode {
    pub const ALL: [FlyMode; 4] = [
        FlyMode::Creative,
        FlyMode::Motion,
        FlyMode::Glide,
        FlyMode::Teleport,
    ];
    pub fn label(self) -> &'static str {
        match self {
            FlyMode::Creative => "Creative",
            FlyMode::Motion => "Motion",
            FlyMode::Glide => "Glide",
            FlyMode::Teleport => "Teleport",
        }
    }
    pub fn note(self) -> &'static str {
        match self {
            FlyMode::Creative => "flips the flight ability; simplest, and fine where flight is allowed",
            FlyMode::Motion => "pure velocity, no ability flag — smooth and continuous",
            FlyMode::Glide => "cancels gravity only; you keep normal walking control",
            FlyMode::Teleport => "steps through the air — fastest, and the one a server is most likely to undo",
        }
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum SpeedMode {
    /// Raise the walking-speed ability. Smooth, and applies to normal movement.
    Abilities,
    /// Add velocity along the direction you are already moving.
    Velocity,
}

impl SpeedMode {
    pub const ALL: [SpeedMode; 2] = [SpeedMode::Abilities, SpeedMode::Velocity];
    pub fn label(self) -> &'static str {
        match self {
            SpeedMode::Abilities => "Abilities",
            SpeedMode::Velocity => "Velocity",
        }
    }
}

/// How a kill aura picks between several valid targets.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum AuraTarget {
    /// Closest first — simple, and what you would hit by hand.
    Nearest,
    /// Finish the wounded one first.
    Weakest,
    /// Whatever is closest to your crosshair, so the aura never swings at
    /// something behind you.
    Angle,
}

impl AuraTarget {
    pub const ALL: [AuraTarget; 3] =
        [AuraTarget::Nearest, AuraTarget::Weakest, AuraTarget::Angle];
    pub fn label(self) -> &'static str {
        match self {
            AuraTarget::Nearest => "Nearest",
            AuraTarget::Weakest => "Weakest",
            AuraTarget::Angle => "Angle",
        }
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum AimMode {
    /// Turn the camera. What you see is what you aim at.
    Camera,
    /// Leave the camera alone and only correct the rotation the client reports.
    Silent,
}

impl AimMode {
    pub const ALL: [AimMode; 2] = [AimMode::Camera, AimMode::Silent];
    pub fn label(self) -> &'static str {
        match self {
            AimMode::Camera => "Camera",
            AimMode::Silent => "Silent",
        }
    }
}

#[derive(Clone)]
pub struct Combat {
    pub kill_aura: bool,
    pub aura_range: f32,
    pub aura_cps: f32,
    pub aura_players: bool,
    pub aura_mobs: bool,
    pub aura_animals: bool,
    pub aura_through_walls: bool,
    pub aura_target: AuraTarget,
    /// Wait for the attack cooldown to recharge before each swing.
    pub aura_cooldown: bool,
    /// Turn toward the target for the swing.
    pub aura_rotate: bool,

    pub aimbot: bool,
    pub aim_mode: AimMode,
    pub aim_fov: f32,
    pub aim_speed: f32,

    pub trigger_bot: bool,
    pub trigger_delay: f32,

    pub reach: bool,
    pub reach_distance: f32,

    pub auto_clicker: bool,
    pub click_cps: f32,
    pub click_jitter: f32,

    pub anti_knockback: bool,
    pub kb_horizontal: f32,
    pub kb_vertical: f32,

    pub criticals: bool,
    /// Upward nudge used to leave the ground. Vanilla's jump is 0.42.
    pub crit_hop: f32,

    pub auto_totem: bool,
    pub auto_shield: bool,

    pub auto_dodge: bool,
    pub dodge_range: f32,
    pub dodge_speed: f32,
    pub dodge_arrows: bool,
    pub dodge_cliffs: bool,
}

impl Default for Combat {
    fn default() -> Self {
        Self {
            kill_aura: false,
            aura_range: 4.0,
            aura_cps: 8.0,
            aura_players: true,
            aura_mobs: true,
            aura_animals: false,
            aura_through_walls: false,
            aura_target: AuraTarget::Angle,
            aura_cooldown: true,
            aura_rotate: true,
            aimbot: false,
            aim_mode: AimMode::Silent,
            aim_fov: 60.0,
            aim_speed: 0.35,
            trigger_bot: false,
            trigger_delay: 0.1,
            reach: false,
            reach_distance: 3.5,
            auto_clicker: false,
            click_cps: 10.0,
            click_jitter: 0.25,
            anti_knockback: false,
            kb_horizontal: 0.0,
            kb_vertical: 0.0,
            criticals: false,
            crit_hop: 0.1,
            auto_totem: false,
            auto_shield: false,
            auto_dodge: false,
            dodge_range: 8.0,
            dodge_speed: 0.28,
            dodge_arrows: true,
            dodge_cliffs: true,
        }
    }
}

#[derive(Clone)]
pub struct Movement {
    pub fly: bool,
    pub fly_mode: FlyMode,
    pub fly_speed: f32,
    pub fly_step: f32,
    pub fly_step_interval: f32,

    pub speed: bool,
    pub speed_mode: SpeedMode,
    pub speed_value: f32,

    pub no_fall: bool,
    pub jetpack: bool,
    pub jetpack_power: f32,
    pub sprint: bool,
    pub noclip: bool,
    pub step: bool,
    pub step_height: f32,
    pub jump_power: bool,
    pub jump_multiplier: f32,

    pub freecam: bool,
    pub freecam_speed: f32,
    pub jesus: bool,
    pub spider: bool,
    pub spider_power: f32,

    /// Dip briefly every so often so the server's floating check resets.
    pub fly_anti_kick: bool,
    pub fly_dip_interval: f32,
    /// Keep velocity inside what a server will accept without correcting you.
    pub speed_limit: bool,
    pub bhop: bool,
    pub blink: bool,
}

impl Default for Movement {
    fn default() -> Self {
        Self {
            fly: false,
            fly_mode: FlyMode::Motion,
            fly_speed: 0.05,
            fly_step: 3.0,
            fly_step_interval: 0.25,
            speed: false,
            speed_mode: SpeedMode::Abilities,
            speed_value: 0.15,
            no_fall: false,
            jetpack: false,
            jetpack_power: 0.4,
            sprint: false,
            noclip: false,
            step: false,
            step_height: 1.0,
            jump_power: false,
            jump_multiplier: 1.5,
            freecam: false,
            freecam_speed: 0.6,
            jesus: false,
            spider: false,
            spider_power: 0.2,
            fly_anti_kick: true,
            fly_dip_interval: 2.0,
            speed_limit: true,
            bhop: false,
            blink: false,
        }
    }
}

#[derive(Clone)]
pub struct Esp {
    pub players: bool,
    pub mobs: bool,
    pub animals: bool,
    pub items: bool,
    pub containers: bool,
    pub xray: bool,
    pub block_radius: f32,
    /// One flag per entry in `blocks::ORE_GROUPS`.
    pub xray_selected: Vec<bool>,
    pub base_finder: bool,
    /// Draw through the game's own 3D renderer instead of projecting to 2D.
    pub gizmo_esp: bool,
    pub show_invis: bool,
    pub show_ping: bool,
    pub show_threat: bool,
    /// Draw where the server still thinks you are.
    pub server_ghost: bool,
    /// The locator bar's data: players the server tracks for you.
    pub player_radar: bool,
    pub base_chunk_radius: f32,
    pub boxes: bool,
    pub tracers: bool,
    pub nametags: bool,
    pub health_bars: bool,
    pub distance: f32,
}

impl Default for Esp {
    fn default() -> Self {
        Self {
            players: false,
            mobs: false,
            animals: false,
            items: false,
            containers: false,
            xray: false,
            block_radius: 24.0,
            // Diamond and ancient debris on by default: the two worth the sweep.
            xray_selected: crate::blocks::ORE_GROUPS
                .iter()
                .enumerate()
                .map(|(i, _)| i < 2)
                .collect(),
            base_finder: false,
            gizmo_esp: false,
            show_invis: true,
            show_ping: true,
            show_threat: true,
            server_ghost: false,
            player_radar: false,
            base_chunk_radius: 8.0,
            boxes: true,
            tracers: false,
            nametags: true,
            health_bars: true,
            distance: 96.0,
        }
    }
}

#[derive(Clone)]
pub struct Visuals {
    pub fullbright: bool,
    pub no_fog: bool,
    pub no_hurt_cam: bool,
    pub no_weather: bool,
    pub no_bob: bool,
    pub no_culling: bool,
    pub view_distance: bool,
    pub view_distance_chunks: f32,
    pub fast_chunks: bool,
    pub fast_chunks_rate: f32,
    pub fov: bool,
    pub fov_value: f32,
    pub watermark: bool,
    pub hud_coords: bool,
    pub hud_modules: bool,
}

impl Default for Visuals {
    fn default() -> Self {
        Self {
            fullbright: false,
            no_fog: false,
            no_hurt_cam: false,
            no_weather: false,
            no_bob: false,
            no_culling: false,
            view_distance: false,
            view_distance_chunks: 16.0,
            fast_chunks: false,
            fast_chunks_rate: 40.0,
            fov: false,
            fov_value: 90.0,
            watermark: false,
            hud_coords: true,
            hud_modules: true,
        }
    }
}

/// Putting blocks where there were none.
#[derive(Clone)]
pub struct Building {
    pub air_place: bool,
    pub auto_build: bool,
    pub place_delay: f32,
}

impl Default for Building {
    fn default() -> Self {
        Self {
            air_place: false,
            auto_build: false,
            place_delay: 0.12,
        }
    }
}

#[derive(Clone, Default)]
pub struct Misc {
    pub hide_from_capture: bool,
    pub auto_respawn: bool,
}

/// Every switch in the menu.
///
/// Client-side only, on purpose: each of these changes how *your* client
/// behaves, which is the whole of what a client can actually do. Nothing here
/// reaches for the integrated server, so nothing here stops working the moment
/// you are not the one running the world.
#[derive(Clone, Default)]
pub struct Config {
    pub combat: Combat,
    pub movement: Movement,
    pub esp: Esp,
    pub building: Building,
    pub visuals: Visuals,
    pub misc: Misc,
    pub ui_scale: f32,
}

impl Config {
    pub fn new() -> Self {
        Self {
            combat: Combat::default(),
            movement: Movement::default(),
            esp: Esp::default(),
            building: Building::default(),
            visuals: Visuals::default(),
            misc: Misc::default(),
            ui_scale: 1.0,
        }
    }
}

/// A highlighted block: which X-Ray group it belongs to.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum BlockKind {
    Ore(usize),
}

/// A block entity worth knowing about — a container, or something that marks
/// out somebody's base.
#[derive(Clone)]
pub struct BaseHit {
    pub x: i32,
    pub y: i32,
    pub z: i32,
    pub label: &'static str,
    /// Shulkers, ender chests, beacons: things nobody leaves lying in a cave.
    pub is_base: bool,
    pub distance: f32,
}

/// One entity worth drawing, handed from the JNI pass to the renderer.
/// World-space only: the projection to screen happens at draw time, where the
/// camera is known.
#[derive(Clone)]
pub struct Target {
    pub min: (f64, f64, f64),
    pub max: (f64, f64, f64),
    pub health: f32,
    pub max_health: f32,
    pub distance: f32,
    pub kind: crate::mc::TargetKind,
    pub name: String,
    /// Hidden from your eyes, but the server still tells you it is there.
    pub invisible: bool,
    /// Round-trip time the server reports for this player, or -1.
    pub ping: i32,
    /// Whether its own reach already covers you.
    pub can_reach_you: bool,
}

/// What the last frame saw in the game, for the readout and the overlay.
#[derive(Clone, Default)]
pub struct GameState {
    pub in_world: bool,
    pub single_player: bool,
    pub pos: (f64, f64, f64),
    pub eye: (f64, f64, f64),
    pub yaw: f32,
    pub pitch: f32,
    pub on_ground: bool,
    pub health: f32,
    pub fps: f32,
    pub loaded_chunks: i32,
    pub sprinting: bool,
    pub screen_open: bool,
    pub fov: f32,
    /// Camera position and rotation, which is what ESP projects from — not the
    /// player, who is somewhere else entirely in third person.
    pub camera: (f64, f64, f64),
    pub camera_yaw: f32,
    pub camera_pitch: f32,
    pub targets: Vec<Target>,
    pub blocks: Vec<(i32, i32, i32, BlockKind)>,
    pub base_hits: Vec<BaseHit>,
    /// Radar blips, in world space.
    pub blips: Vec<(f64, f64, f64, bool)>,
    /// Where the server still thinks you are, and how far that is from where
    /// you actually are.
    pub ghost: Option<((f64, f64, f64), f32)>,
}

/// Every toggleable module, with the config field it drives.
///
/// One list, generated once: it gives the menu its rows, keybinds something to
/// bind to, and the config file something to name — so a new module cannot be
/// added to one of those and forgotten in the others.
macro_rules! modules {
    ($($variant:ident => $label:literal, $key:literal, $($path:ident).+;)*) => {
        #[derive(Clone, Copy, PartialEq, Eq, Debug)]
        pub enum ModuleId { $($variant),* }

        impl ModuleId {
            pub const ALL: &'static [ModuleId] = &[$(ModuleId::$variant),*];

            pub fn label(self) -> &'static str {
                match self { $(ModuleId::$variant => $label),* }
            }

            /// Stable name used in the config file.
            pub fn key(self) -> &'static str {
                match self { $(ModuleId::$variant => $key),* }
            }

            pub fn get(self, cfg: &Config) -> bool {
                match self { $(ModuleId::$variant => cfg.$($path).+),* }
            }

            pub fn set(self, cfg: &mut Config, value: bool) {
                match self { $(ModuleId::$variant => cfg.$($path).+ = value),* }
            }

            pub fn toggle(self, cfg: &mut Config) {
                let v = self.get(cfg);
                self.set(cfg, !v);
            }

            pub fn from_key(name: &str) -> Option<ModuleId> {
                Self::ALL.iter().copied().find(|m| m.key() == name)
            }
        }
    };
}

modules! {
    Fly          => "Fly",               "fly",          movement.fly;
    Speed        => "Speed",             "speed",        movement.speed;
    NoFall       => "No Fall Damage",    "nofall",       movement.no_fall;
    Jetpack      => "Jetpack",           "jetpack",      movement.jetpack;
    Sprint       => "Auto Sprint",       "sprint",       movement.sprint;
    Noclip       => "Noclip",            "noclip",       movement.noclip;
    Step         => "Step",              "step",         movement.step;
    HighJump     => "High Jump",         "highjump",     movement.jump_power;
    Freecam      => "Freecam",           "freecam",      movement.freecam;
    Jesus        => "Jesus",             "jesus",        movement.jesus;
    Spider       => "Spider",            "spider",       movement.spider;
    Bhop         => "Bhop",              "bhop",         movement.bhop;
    Blink        => "Blink",             "blink",        movement.blink;

    KillAura     => "Kill Aura",         "killaura",     combat.kill_aura;
    Aimbot       => "Aimbot",            "aimbot",       combat.aimbot;
    TriggerBot   => "Trigger Bot",       "triggerbot",   combat.trigger_bot;
    Reach        => "Reach",             "reach",        combat.reach;
    AutoClicker  => "Auto Clicker",      "autoclicker",  combat.auto_clicker;
    AntiKnockback=> "Anti Knockback",    "antikb",       combat.anti_knockback;
    Criticals    => "Criticals",         "criticals",    combat.criticals;
    AutoTotem    => "Auto Totem",        "autototem",    combat.auto_totem;
    AutoShield   => "Auto Shield",       "autoshield",   combat.auto_shield;
    AutoDodge    => "Auto Dodge",        "autododge",    combat.auto_dodge;

    EspPlayers   => "Players",           "esp_players",  esp.players;
    EspMobs      => "Entities",          "esp_mobs",     esp.mobs;
    EspAnimals   => "Animals",           "esp_animals",  esp.animals;
    EspItems     => "Dropped Items",     "esp_items",    esp.items;
    EspContainers=> "Containers",        "esp_chests",   esp.containers;
    Xray         => "X-Ray",             "xray",         esp.xray;
    BaseFinder   => "Base Finder",       "basefinder",   esp.base_finder;
    GizmoEsp     => "3D Boxes",          "gizmoesp",     esp.gizmo_esp;
    ShowInvis    => "Show Invisible",    "showinvis",    esp.show_invis;
    ShowPing     => "Show Ping",         "showping",     esp.show_ping;
    ShowThreat   => "Threat Range",      "showthreat",   esp.show_threat;
    ServerGhost  => "Server Ghost",      "serverghost",  esp.server_ghost;
    PlayerRadar  => "Player Radar",      "playerradar",  esp.player_radar;
    EspBoxes     => "Boxes",             "esp_boxes",    esp.boxes;
    EspTracers   => "Tracers",           "esp_tracers",  esp.tracers;
    EspNametags  => "Nametags",          "esp_names",    esp.nametags;
    EspHealth    => "Health Bars",       "esp_health",   esp.health_bars;

    Fullbright   => "Fullbright",        "fullbright",   visuals.fullbright;
    NoFog        => "No Fog",            "nofog",        visuals.no_fog;
    NoHurtCam    => "No Hurt Camera",    "nohurtcam",    visuals.no_hurt_cam;
    NoWeather    => "No Weather",        "noweather",    visuals.no_weather;
    NoBob        => "No View Bob",       "nobob",        visuals.no_bob;
    NoCulling    => "No Culling",        "noculling",    visuals.no_culling;
    ViewDistance => "Extend View",       "viewdistance", visuals.view_distance;
    FastChunks   => "Fast Chunks",       "fastchunks",   visuals.fast_chunks;
    CustomFov    => "Custom FOV",        "fov",          visuals.fov;
    Watermark    => "Watermark",         "watermark",    visuals.watermark;
    HudCoords    => "Coordinates",       "hud_coords",   visuals.hud_coords;
    HudModules   => "Module List",       "hud_modules",  visuals.hud_modules;

    AirPlace     => "Air Place",         "airplace",     building.air_place;
    AutoBuild    => "Auto Build",        "autobuild",    building.auto_build;

    HideCapture  => "Hide From Capture", "hidecapture",  misc.hide_from_capture;
    AutoRespawn  => "Auto Respawn",      "autorespawn",  misc.auto_respawn;
}

/// A key bound to a module, or to the panic switch.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum BindTarget {
    Module(ModuleId),
    Panic,
}

#[derive(Clone)]
pub struct Bind {
    pub key: u32,
    pub target: BindTarget,
}

pub struct Shared {
    pub menu_open: bool,
    pub cfg: Config,
    pub game: GameState,
    pub events: Vec<egui::Event>,
    pub pointer: egui::Pos2,
    pub scale: f32,
    pub eject: bool,
    /// Anything that failed to resolve, shown in the menu so a rename is
    /// obvious instead of silent.
    pub missing: Vec<String>,
    pub status: String,
    pub binds: Vec<Bind>,
    /// Set while the menu is waiting for the next key press to bind.
    pub binding: Option<BindTarget>,
}

impl Shared {
    /// Act on a key press. Returns true if it was ours.
    pub fn handle_key(&mut self, vk: u32) -> bool {
        // A bind in progress swallows the next key, whatever it is.
        if let Some(target) = self.binding.take() {
            self.binds.retain(|b| b.key != vk && b.target != target);
            // Escape clears a bind rather than setting one.
            if vk != 0x1B {
                self.binds.push(Bind { key: vk, target });
            }
            return true;
        }
        let Some(bind) = self.binds.iter().find(|b| b.key == vk).cloned() else {
            return false;
        };
        match bind.target {
            BindTarget::Module(m) => m.toggle(&mut self.cfg),
            BindTarget::Panic => self.panic_off(),
        }
        true
    }

    /// Turn everything off at once.
    pub fn panic_off(&mut self) {
        for m in ModuleId::ALL {
            // The HUD is not a cheat; leave it alone.
            if matches!(
                m,
                ModuleId::Watermark | ModuleId::HudCoords | ModuleId::HudModules
                    | ModuleId::EspBoxes | ModuleId::EspNametags | ModuleId::EspHealth
            ) {
                continue;
            }
            m.set(&mut self.cfg, false);
        }
    }

    pub fn bind_for(&self, target: BindTarget) -> Option<u32> {
        self.binds.iter().find(|b| b.target == target).map(|b| b.key)
    }

    pub fn toggle_menu(&mut self) {
        self.menu_open = !self.menu_open;
        // Drop half-finished input so the menu never opens mid-drag.
        self.events.clear();
    }
}

static SHARED: OnceLock<Mutex<Shared>> = OnceLock::new();

fn cell() -> &'static Mutex<Shared> {
    SHARED.get_or_init(|| {
        Mutex::new(Shared {
            menu_open: false,
            cfg: Config::new(),
            game: GameState::default(),
            events: Vec::new(),
            pointer: egui::Pos2::ZERO,
            scale: 1.0,
            eject: false,
            missing: Vec::new(),
            status: "starting".into(),
            binds: Vec::new(),
            binding: None,
        })
    })
}

/// Run `f` against the shared state. Returns None only if the lock is poisoned,
/// which would mean a panic already unwound through it.
pub fn with<R>(f: impl FnOnce(&mut Shared) -> R) -> Option<R> {
    cell().lock().ok().map(|mut s| f(&mut s))
}