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:
Single File
Directory with Main
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. Create a directory with a main file for complex scripts: scripts/
└── my-complex-script/
├── main.js # Entry point
├── helpers.js
├── config.json
└── README.md
The main.js (or main.py, etc.) file is the entry point.
Reference: ScriptManager.kt:74-102
Script API
The Script API provides access to LiquidBounce’s internal systems through bindings:
Available Bindings
Client Context (ScriptContextProvider)
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
Module System (ScriptModule)
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
Command System (ScriptCommandBuilder)
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 );
Async Operations (ScriptAsyncUtil)
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:
Complete Module Example
Python Module Example
// 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:
Common Events
Combat Events
Module Events
// 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
});
// Attack entity
module . on ( "AttackEntity" , ( event ) => {
client . chat ( `Attacking ${ event . entity . getName () } ` );
});
// Take damage
module . on ( "PlayerDamage" , ( event ) => {
const damage = event . damage ;
const source = event . source ;
});
// Critical hit
module . on ( "CriticalHit" , ( event ) => {
// event.entity - the attacked entity
});
// Module lifecycle
module . on ( "enable" , () => {
// Called when module is enabled
});
module . on ( "disable" , () => {
// Called when module is disabled
});
// Other module toggles
module . on ( "ModuleToggle" , ( event ) => {
client . chat ( ` ${ event . module } ${ event . enabled ? "enabled" : "disabled" } ` );
});
See event/EVENT_NAME_TO_CLASS in source for all available events
Script Lifecycle
Scripts follow a defined lifecycle managed by the ScriptManager:
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
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
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
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