Skip to main content

Overview

LiquidBounce’s script system allows you to extend the client with custom functionality using various programming languages. Built on GraalVM Polyglot, the system supports JavaScript, Python, Ruby, R, and more, enabling you to create modules, commands, and event handlers without recompiling the client.
Scripts are stored in the scripts/ directory and loaded automatically on client startup

Script Manager

The ScriptManager handles all script lifecycle operations including loading, enabling, disabling, and unloading scripts.
object ScriptManager {
    val scripts = mutableSetOf<PolyglotScript>()
    val root = File(ConfigSystem.rootFolder, "scripts")
    
    fun loadAll()
    fun unloadAll()
    fun reload()
}
Source: script/ScriptManager.kt:40-198

Supported Languages

The script system uses GraalVM’s polyglot engine to support multiple languages:
// main.js
const module = {
    name: "MyModule",
    category: "Combat",
    description: "My custom module",
    
    settings: {
        range: value.float("Range", 4.0, 1.0, 6.0),
        autoBlock: value.boolean("AutoBlock", true)
    }
};

client.registerModule(module);

module.on("enable", () => {
    client.chat("Module enabled!");
});

module.on("GameTick", (event) => {
    // Your tick logic here
});
The engine automatically detects the language from file extension (.js, .py, .rb, etc.)

Script Structure

Scripts can be organized in two ways:
Place a script file directly in the scripts/ folder:
scripts/
├── my-script.js
├── another-script.py
└── utils.rb
Each file is loaded as a separate script.
Reference: ScriptManager.kt:74-102

Script API

The Script API provides access to LiquidBounce’s internal systems through bindings:

Available Bindings

Access Minecraft client instances and game state:
const mc = client.minecraft;  // Minecraft instance
const player = client.player; // Local player
const world = client.world;   // Current world
const network = client.network; // Network handler
Source: script/bindings/api/ScriptContextProvider.kt
Create custom modules with settings and event handlers:
const myModule = {
    name: "CustomKillAura",
    category: "Combat",
    tag: "4.0",
    description: "My custom KillAura implementation",
    
    settings: {
        range: value.float("Range", 4.0, 1.0, 6.0),
        targets: value.multiEnum("Targets", ["Players", "Mobs"])
    }
};

client.registerModule(myModule);
Source: script/bindings/features/ScriptModule.kt:40-77
Register custom commands:
const greetCommand = {
    name: "greet",
    aliases: ["hello", "hi"],
    parameters: [
        { name: "player", required: true }
    ],
    execute: (context) => {
        const player = context.get("player");
        client.chat(`Hello, ${player}!`);
    }
};

client.registerCommand(greetCommand);
Source: script/bindings/features/ScriptCommandBuilder.kt
Helper functions for common operations:
  • ScriptMovementUtil - Player movement helpers
  • ScriptRotationUtil - Rotation calculations
  • ScriptBlockUtil - Block interaction utilities
  • ScriptItemUtil - Item manipulation
  • ScriptNetworkUtil - Network packet handling
  • ScriptInteractionUtil - Entity interaction
// Rotation example
const rotation = util.rotation.calculateRotation(
    player.position,
    target.position
);
util.rotation.applyRotation(rotation);
Schedule tasks on the game thread:
// Run after 20 ticks (1 second)
async.schedule(() => {
    client.chat("1 second has passed!");
}, 20);

// Run on next tick
async.nextTick(() => {
    // Do something
});
Source: script/bindings/api/ScriptAsyncUtil.kt

Creating Script Modules

Here’s a complete example of a script module:
// AutoGreet.js - Automatically greets players who join
const module = {
    name: "AutoGreet",
    category: "Misc",
    description: "Automatically greets players when they join",
    
    settings: {
        message: value.text("Message", "Welcome, {player}!"),
        delay: value.int("Delay", 1000, 0, 5000),
        onlyFriends: value.boolean("OnlyFriends", false)
    }
};

client.registerModule(module);

let greetedPlayers = new Set();

module.on("enable", () => {
    greetedPlayers.clear();
    client.chat("AutoGreet enabled!");
});

module.on("disable", () => {
    greetedPlayers.clear();
});

module.on("PlayerJoin", (event) => {
    const player = event.player;
    const playerName = player.getName();
    
    // Skip if already greeted
    if (greetedPlayers.has(playerName)) return;
    
    // Check friends filter
    if (module.settings.onlyFriends.get() && !client.isFriend(playerName)) {
        return;
    }
    
    // Schedule greeting
    async.schedule(() => {
        const message = module.settings.message.get()
            .replace("{player}", playerName);
        client.sendMessage(message);
        greetedPlayers.add(playerName);
    }, module.settings.delay.get());
});

module.on("WorldChange", (event) => {
    // Clear greeted players on world change
    greetedPlayers.clear();
});

Event System

Scripts can listen to game events using the event system:
// Game tick (20 times per second)
module.on("GameTick", (event) => {
    // Runs every tick
});

// Player movement
module.on("PlayerMove", (event) => {
    // Access: event.from, event.to
});

// World render
module.on("WorldRender", (event) => {
    // Render custom overlays
});

// Packet events
module.on("PacketSend", (event) => {
    // Intercept outgoing packets
    // event.cancel() to prevent sending
});
See event/EVENT_NAME_TO_CLASS in source for all available events

Script Lifecycle

Scripts follow a defined lifecycle managed by the ScriptManager:
1

Initialization

The script engine is initialized with GraalVM polyglot support:
fun initializeEngine() {
    val engine = Engine.create()
    logger.info("Supported languages: ${engine.languages.keys.joinToString(", ")}")
}
Reference: ScriptManager.kt:58-68
2

Loading

Scripts are loaded from the scripts directory:
fun loadAll() {
    val files = root.listFiles { file ->
        Source.findLanguage(file) != null || file.isDirectory
    }
    
    files.forEach { file ->
        if (file.isDirectory) {
            // Look for main file
        } else {
            loadCatched(file)
        }
    }
}
Reference: ScriptManager.kt:74-102
3

Enabling

Loaded scripts are enabled, registering their modules and commands:
fun enableAll() {
    scripts.forEach(PolyglotScript::enable)
    
    if (scripts.isNotEmpty()) {
        mc.execute(ModuleClickGui::sync)
    }
}
Reference: ScriptManager.kt:163-170
4

Reloading

Scripts can be hot-reloaded without restarting the client:
fun reload() {
    disableAll()
    unloadAll()
    loadAll()
    enableAll()
}
Reference: ScriptManager.kt:183-197

Managing Scripts

Use the script command to manage loaded scripts:
# Reload all scripts
.script reload

# List loaded scripts
.script list

# Load a specific script
.script load my-script.js

# Unload a script
.script unload my-script

Best Practices

Use Main Files

Organize complex scripts in directories with a main entry point

Handle Errors

Wrap event handlers in try-catch to prevent script crashes

Clean Up Resources

Always clean up in the disable event handler

Test Thoroughly

Test scripts in single-player before using on servers

Use Settings

Make scripts configurable with the value system

Document Code

Add comments and descriptions for maintainability
Scripts have full access to the client internals. Only load scripts from trusted sources.

Marketplace Integration

Scripts can be distributed through the LiquidBounce Marketplace:
MarketplaceManager.getSubscribedItemsOfType(MarketplaceItemType.SCRIPT)
    .map { item -> item.getInstallationFolder() }
Reference: ScriptManager.kt:78-80

Debugging Scripts

Enable debug options for script development:
val script = PolyglotScript(
    language,
    file,
    debugOptions = ScriptDebugOptions(
        enableDebugger = true,
        debugPort = 9229
    )
)
Then connect with Chrome DevTools or your IDE’s debugger.

Next Steps

Module System

Understand the module architecture

Command System

Learn about command creation

Script Examples

Explore complete script examples

API Reference

Complete scripting API documentation

Build docs developers (and LLMs) love