The PolyglotScript class represents a single loaded script instance in LiquidBounce. It manages the script’s lifecycle, context, registered features, and event handling.
Class Declaration
class PolyglotScript(
val language: String,
val file: File,
val debugOptions: ScriptDebugOptions = ScriptDebugOptions()
) : AutoCloseable
The programming language of the script (e.g., “js”, “python”, “ruby”)
Debug configuration for the script
Properties
These properties are set when registerScript() is called from within the script.
Display name of the script
Example from script:
registerScript({
name: "MyAwesomeScript",
version: "2.1.0",
authors: ["Author1", "Author2"]
});
Context
The GraalVM polyglot context for executing script code.
private val context: Context
The context is configured with:
- Host Access: Full access to Java classes
- IO Access: File system and network operations
- Working Directory: Script’s parent directory
- Thread Creation: Enabled
- Native Access: Disabled for security
- Language Options: Language-specific features
Script State
Whether the script is currently enabled
Registered Features
Scripts can register various features that are tracked by the instance:
registeredModules
MutableList<ClientModule>
Modules created by the script
Commands created by the script
Modes created by the script
Methods
initScript()
Initializes the script by evaluating its source code.
Initialization process:
- Evaluates the script source code
- Calls the global
load event
- Validates required metadata (name, version, authors)
- Logs loading time
Throws: Exception if script evaluation fails or metadata is missing
Logged output:
[ScriptAPI] Successfully loaded script 'my-script.js' in 42ms.
registerModule()
Registers a new module from the script.
fun registerModule(
moduleObject: Map<String, Any>,
callback: Consumer<ClientModule>
)
JavaScript object containing module configuration
callback
Consumer<ClientModule>
required
Function called with the created module instance
Module object structure:
{
name: "ModuleName",
category: "Combat",
description: "Optional description",
tag: "Optional tag",
settings: {
settingName: Setting.type(...)
}
}
Example:
script.registerModule({
name: "AutoClicker",
category: "Combat"
}, module => {
module.on("enable", () => {
// Enable logic
});
});
registerCommand()
Registers a new command from the script.
fun registerCommand(commandObject: Value)
GraalVM Value containing command configuration
Example:
script.registerCommand({
name: "test",
aliases: ["t"],
parameters: [
{
name: "arg",
required: true,
validate: (value) => ({
accept: value.length > 0,
value: value,
error: "Argument cannot be empty"
})
}
],
onExecute: (arg) => {
Client.displayChatMessage("Received: " + arg);
}
});
registerMode()
Registers a new mode for an existing mode value group.
fun registerMode(
modeValueGroup: ModeValueGroup<Mode>,
modeObject: Map<String, Any>,
callback: Consumer<Mode>
)
modeValueGroup
ModeValueGroup<Mode>
required
The mode group to add the mode to
Mode configuration object
Function called with the created mode instance
Example:
const killAura = Client.moduleManager.getModule("KillAura");
const rotationMode = killAura.settings.rotationMode;
script.registerMode(rotationMode, {
name: "CustomRotation"
}, mode => {
// Configure mode
});
on()
Registers a global event handler.
fun on(eventName: String, handler: Runnable)
Name of the event to listen to
Function to call when the event fires
Global events:
load - Called when script is loaded
enable - Called when script is enabled
disable - Called when script is disabled
Example:
script.on("load", () => {
console.log("Script loaded!");
});
script.on("enable", () => {
console.log("Script enabled!");
});
enable()
Enables the script and all its registered features.
Behavior:
- Calls global
enable event
- Registers all modules with ModuleManager
- Registers all commands with CommandManager
- Adds all modes to their respective mode groups
- Sets
scriptEnabled to true
Idempotent - calling when already enabled has no effect.
disable()
Disables the script and all its registered features.
Behavior:
- Calls global
disable event
- Unregisters all modules from ModuleManager
- Unregisters all commands from CommandManager
- Removes all modes from their mode groups
- Triggers RefreshArrayListEvent
- Sets
scriptEnabled to false
Idempotent - calling when already disabled has no effect.
close()
Closes the script context and releases resources.
Behavior:
- Closes the GraalVM context
- Releases all script resources
- Should be called before removing script from memory
Implements AutoCloseable for use with try-with-resources.
Context Configuration
The script context is configured differently based on language:
JavaScript Features
if (language == "js") {
option("js.nashorn-compat", "true")
option("js.ecmascript-version", "2023")
option("js.commonjs-require", "true")
option("js.commonjs-require-cwd", file.parentFile.absolutePath)
}
Features:
- Nashorn compatibility mode
- ECMAScript 2023 support
- CommonJS require() support
- Module resolution from script directory
Example:
// Import other JavaScript files
const utils = require('./utils.js');
const config = require('./config.json');
Debug Options
data class ScriptDebugOptions(
val enabled: Boolean = false,
val protocol: DebugProtocol = DebugProtocol.INSPECT,
val port: Int = 9229,
val suspendOnStart: Boolean = false,
val inspectInternals: Boolean = true
)
Enable debugging for the script
protocol
DebugProtocol
default:"INSPECT"
Debug protocol to use (INSPECT or DAP)
Port for debug connection
Pause execution at script start
Allow inspecting internal variables
INSPECT Protocol
Chrome DevTools Protocol support:
devtools://devtools/bundled/js_app.html?ws=127.0.0.1:9229/script-name.js
Use Chrome/Edge DevTools to debug JavaScript scripts.
DAP Protocol
Debug Adapter Protocol for VS Code and other editors:
{
"type": "node",
"request": "attach",
"name": "Attach to LiquidBounce Script",
"port": 9229
}
Lifecycle Example
// Load script
val script = ScriptManager.loadScript(
file = File("my-script.js"),
language = "js",
debugOptions = ScriptDebugOptions(enabled = true)
)
// Script is now loaded and initialized
// Metadata is available
println("${script.scriptName} v${script.scriptVersion}")
// Enable script
script.enable()
// Script features are now active
val modules = script.registeredModules
val commands = script.registeredCommands
// Disable script
script.disable()
// Features are deactivated but script remains in memory
// Unload script
script.close()
ScriptManager.scripts.remove(script)
// Script is fully unloaded
Error Handling
Errors during event handling are caught and logged:
private fun callGlobalEvent(eventName: String) {
try {
globalEvents[eventName]?.run()
} catch (throwable: Throwable) {
logger.error(
"${file.name}::$scriptName -> Event Function $eventName threw an error",
throwable
)
}
}
This prevents a single faulty event handler from crashing the entire script.
See Also