Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/CCBlueX/LiquidBounce/llms.txt

Use this file to discover all available pages before exploring further.

Introduction

Modules are the core feature type in LiquidBounce. Scripts can create custom modules that appear in the ClickGUI and can be toggled on/off just like built-in modules.

Basic Module

Here’s a simple module that sends a message when enabled:
SimpleModule.js
registerScript({
    name: "SimpleModule",
    version: "1.0.0",
    authors: ["YourName"]
});

registerModule({
    name: "HelloModule",
    category: "Misc",
    description: "Says hello when enabled"
}, (module) => {
    module.on("enable", () => {
        Client.displayChatMessage("Hello! Module enabled.");
    });
    
    module.on("disable", () => {
        Client.displayChatMessage("Goodbye! Module disabled.");
    });
});

Module Properties

When registering a module, provide these properties:
registerModule({
    name: "ModuleName",           // Required: Display name
    category: "Combat",           // Required: Module category
    description: "Description",   // Optional: Module description
    tag: "TagText",              // Optional: Tag shown in ArrayList
    settings: { }                // Optional: Module settings
}, (module) => {
    // Module initialization
});

Categories

Available module categories:
  • "Combat" - Combat-related modules
  • "Movement" - Movement modules
  • "Player" - Player-related modules
  • "World" - World interaction modules
  • "Render" - Visual/rendering modules
  • "Client" - Client-side modules
  • "Misc" - Miscellaneous modules
  • "Fun" - Fun/troll modules

Module Settings

Add configurable settings to your module:
SettingsExample.js
registerScript({
    name: "SettingsExample",
    version: "1.0.0",
    authors: ["YourName"]
});

registerModule({
    name: "CustomAura",
    category: "Combat",
    description: "Custom combat module",
    settings: {
        range: Setting.float({
            name: "Range",
            default: 4.2,
            range: [1.0, 6.0],
            suffix: " blocks"
        }),
        delay: Setting.int({
            name: "Delay",
            default: 100,
            range: [0, 1000],
            suffix: "ms"
        }),
        targetPlayers: Setting.boolean({
            name: "TargetPlayers",
            default: true
        }),
        mode: Setting.choose({
            name: "Mode",
            choices: ["Single", "Multi", "Switch"],
            default: "Single"
        })
    }
}, (module) => {
    // Access settings
    module.on("enable", () => {
        const range = module.settings.range.value;
        const delay = module.settings.delay.value;
        const targetPlayers = module.settings.targetPlayers.value;
        const mode = module.settings.mode.value;
        
        Client.displayChatMessage(
            "Range: " + range + ", Delay: " + delay
        );
    });
});

Setting Types

See the API Reference for all available setting types:
  • Setting.boolean() - True/false toggle
  • Setting.int() - Integer with range
  • Setting.float() - Decimal number with range
  • Setting.intRange() - Range of integers (min-max)
  • Setting.floatRange() - Range of floats (min-max)
  • Setting.text() - Text input
  • Setting.textArray() - Array of text values
  • Setting.choose() - Single choice from options
  • Setting.multiChoose() - Multiple choices
  • Setting.key() - Keybinding

Event Handlers

Modules can listen to various game events:
EventModule.js
registerScript({
    name: "EventModule",
    version: "1.0.0",
    authors: ["YourName"]
});

registerModule({
    name: "EventDemo",
    category: "Misc"
}, (module) => {
    // Called when module is enabled
    module.on("enable", () => {
        Client.displayChatMessage("Module enabled!");
    });
    
    // Called when module is disabled
    module.on("disable", () => {
        Client.displayChatMessage("Module disabled!");
    });
    
    // Called every game tick
    module.on("playerTick", () => {
        if (mc.player === null) return;
        
        // Your per-tick logic here
    });
    
    // Called when player moves
    module.on("playerMove", (event) => {
        // event.movement contains the movement vector
        const movement = event.movement;
        
        // You can modify the movement
        // event.movement = new Vec3d(0, movement.y, 0);
    });
    
    // Called when receiving a packet
    module.on("packetReceive", (event) => {
        // Access packet data
        // You can cancel the packet
        // event.cancelEvent();
    });
});

Common Events

Player Events:
  • playerTick - Every player tick (20 times per second)
  • playerMove - When player moves
  • playerJump - When player jumps
  • healthUpdate - When health changes
  • death - When player dies
Network Events:
  • packetSend - Before sending a packet
  • packetReceive - After receiving a packet
Render Events:
  • gameRender - During game rendering
  • worldRender - During world rendering
  • overlayRender - During HUD/overlay rendering
World Events:
  • blockBreak - When breaking a block
  • blockPlace - When placing a block
See the API Reference for a complete event list.

Dynamic Module Tag

Update the module’s tag dynamically (shown in ArrayList):
registerModule({
    name: "SpeedCounter",
    category: "Render",
    tag: "0.00"
}, (module) => {
    module.on("playerTick", () => {
        if (mc.player === null) return;
        
        const speed = MovementUtil.getSpeed();
        // Update the tag
        module.tag = speed.toFixed(2);
    });
});

Complete Module Example

Here’s a complete combat module:
CustomKillAura.js
registerScript({
    name: "CustomKillAura",
    version: "1.0.0",
    authors: ["YourName"]
});

registerModule({
    name: "CustomKillAura",
    category: "Combat",
    description: "Custom kill aura implementation",
    settings: {
        range: Setting.float({
            name: "Range",
            default: 4.2,
            range: [1.0, 6.0],
            suffix: " blocks"
        }),
        delay: Setting.intRange({
            name: "Delay",
            default: [50, 100],
            range: [0, 500],
            suffix: "ms"
        }),
        targetPlayers: Setting.boolean({
            name: "TargetPlayers",
            default: true
        }),
        targetMobs: Setting.boolean({
            name: "TargetMobs",
            default: false
        }),
        rotations: Setting.boolean({
            name: "Rotations",
            default: true
        })
    }
}, (module) => {
    let lastAttack = 0;
    let currentTarget = null;
    
    module.on("enable", () => {
        lastAttack = 0;
        currentTarget = null;
    });
    
    module.on("disable", () => {
        currentTarget = null;
    });
    
    module.on("playerTick", () => {
        const player = mc.player;
        const world = mc.level;
        
        if (player === null || world === null) return;
        
        // Get settings
        const range = module.settings.range.value;
        const delayMin = module.settings.delay.value.start;
        const delayMax = module.settings.delay.value.endInclusive;
        const targetPlayers = module.settings.targetPlayers.value;
        const targetMobs = module.settings.targetMobs.value;
        const useRotations = module.settings.rotations.value;
        
        // Find target
        currentTarget = findTarget(world, player, range, targetPlayers, targetMobs);
        
        if (currentTarget === null) {
            module.tag = "No Target";
            return;
        }
        
        // Update tag
        module.tag = currentTarget.getName().getString();
        
        // Check delay
        const now = Date.now();
        const randomDelay = delayMin + Math.random() * (delayMax - delayMin);
        
        if (now - lastAttack < randomDelay) {
            return;
        }
        
        // Aim at target
        if (useRotations) {
            const rotation = RotationUtil.newRotationEntity(currentTarget);
            RotationUtil.aimAtRotation(rotation, true);
        }
        
        // Attack
        mc.gameMode.attack(player, currentTarget);
        player.swing(Hand.MAIN_HAND);
        
        lastAttack = now;
    });
    
    function findTarget(world, player, range, targetPlayers, targetMobs) {
        let closestTarget = null;
        let closestDistance = range * range; // Squared for performance
        
        const entities = world.entitiesForRendering();
        
        for (let i = 0; i < entities.size(); i++) {
            const entity = entities.get(i);
            
            // Skip self
            if (entity === player) continue;
            
            // Check entity type
            if (entity.getType().toString().includes("Player")) {
                if (!targetPlayers) continue;
            } else if (entity instanceof Java.type("net.minecraft.world.entity.Mob")) {
                if (!targetMobs) continue;
            } else {
                continue;
            }
            
            // Check distance
            const distSq = player.distanceToSqr(entity);
            if (distSq < closestDistance) {
                closestDistance = distSq;
                closestTarget = entity;
            }
        }
        
        return closestTarget;
    }
});

Best Practices

  1. Always check for null - mc.player and mc.level can be null
  2. Clean up on disable - Reset variables in the disable event
  3. Use efficient algorithms - Avoid expensive operations in playerTick
  4. Handle errors - Wrap risky code in try-catch blocks
  5. Meaningful tags - Update module.tag to show useful info
  6. Reasonable defaults - Choose sensible default values for settings

Advanced: Module Modes

For complex modules, you can register sub-modes. See the source code reference in script/bindings/features/ScriptMode.kt for details on using registerMode().

Next Steps