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:
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:
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:
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:
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
- Always check for null -
mc.player and mc.level can be null
- Clean up on disable - Reset variables in the
disable event
- Use efficient algorithms - Avoid expensive operations in
playerTick
- Handle errors - Wrap risky code in try-catch blocks
- Meaningful tags - Update
module.tag to show useful info
- 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