Skip to main content
This guide provides tips and techniques for debugging LiquidBounce during development.

Development Environment

IDE Setup

IntelliJ IDEA offers the best Kotlin debugging experience:
  1. Open as Gradle Project
    • File → Open → Select LiquidBounce directory
    • Let Gradle sync complete
  2. Generate Sources (Optional but Recommended)
    ./gradlew genSources
    
    This decompiles Minecraft code for easier debugging.
  3. Enable Auto-Import
    • Settings → Build, Execution, Deployment → Build Tools → Gradle
    • Enable “Auto-import”

Running with Debugger

Method 1: Gradle Task

  1. Open Gradle panel (View → Tool Windows → Gradle)
  2. Navigate to: LiquidBounce → fabric → runClient
  3. Right-click → Debug ‘LiquidBounce [runClient]‘

Method 2: Run Configuration

  1. Edit Configurations+Gradle
  2. Configure:
    • Name: Debug LiquidBounce Client
    • Gradle project: LiquidBounce
    • Tasks: runClient
    • VM options: -Xmx4G (optional, for more memory)
  3. Click Debug button

Debugging Techniques

Breakpoints

Set breakpoints in your code to pause execution:
fun onEnable() {
    // Set breakpoint on next line
    player.sendMessage("Module enabled")
}
Setting Breakpoints:
  • Click in the left margin next to the line number
  • Or press Ctrl+F8 (Windows/Linux) or ⌘+F8 (Mac)

Conditional Breakpoints

Break only when certain conditions are met:
  1. Right-click a breakpoint
  2. Enter condition (e.g., player.health < 5.0)
Useful for debugging specific scenarios without stopping every time.

Logging Breakpoints

Log messages without stopping execution:
  1. Right-click a breakpoint
  2. Check “Evaluate and log”
  3. Enter expression to log
  4. Uncheck “Suspend”

Evaluate Expression

While paused at a breakpoint:
  • Alt+F8 (Windows/Linux) or ⌥+F8 (Mac)
  • Type any Kotlin/Java expression
  • See immediate results
Examples:
player.inventory.mainHandStack
world.players.size
mc.currentScreen?.javaClass?.simpleName

Console Logging

Using LiquidBounce Logger

import net.ccbluex.liquidbounce.utils.client.logger

// Info level
logger.info("Player position: ${player.pos}")

// Debug level
logger.debug("Checking target: $targetEntity")

// Warning level
logger.warn("Invalid configuration value")

// Error level
logger.error("Failed to load module", exception)

Minecraft Logger

import org.apache.logging.log4j.LogManager

val LOGGER = LogManager.getLogger("LiquidBounce")

LOGGER.info("Custom log message")

Chat Messages (In-Game Debugging)

import net.ccbluex.liquidbounce.utils.client.chat

// Simple message
chat("Debug: $value")

// Colored message
chat("§aSuccess: §7$message")

// Without prefix
chat(message, prefix = false)

Common Debugging Scenarios

Debugging Modules

Module Not Working

  1. Check if enabled
    logger.info("Module enabled: $enabled")
    
  2. Verify event handlers
    val attackHandler = handler<AttackEvent> { event ->
        logger.info("Attack event triggered")
        // Your code
    }
    
  3. Check conditions
    if (!enabled) {
        logger.debug("Module disabled, skipping")
        return@handler
    }
    

Module Settings Not Saving

  1. Check config serialization
  2. Verify value changes:
    val range by float("Range", 4.0f, 1.0f..6.0f)
        .onChange { oldValue, newValue ->
            logger.info("Range changed: $oldValue -> $newValue")
            true // Accept change
        }
    

Debugging Events

Event Not Firing

  1. Verify event handler registration
    val handler = handler<PlayerTickEvent> { event ->
        logger.info("Tick event fired")
    }
    
  2. Check event sequence
    val handler = sequenceHandler<PlayerTickEvent> { event ->
        logger.info("Sequence handler called")
        // Your code
    }
    
  3. Ensure module is enabled - Handlers typically only fire when module is enabled

Event Firing Too Often

Use throttling or debouncing:
private var lastCheck = 0L

val handler = handler<PlayerTickEvent> {
    val now = System.currentTimeMillis()
    if (now - lastCheck < 1000) return@handler // Throttle to 1 second
    lastCheck = now
    
    // Your code
}

Debugging Rendering

Rendering Not Showing

  1. Check render event
    val renderHandler = handler<WorldRenderEvent> { event ->
        logger.debug("Render event triggered")
        renderEnvironmentForWorld(event.matrixStack) {
            // Your rendering
        }
    }
    
  2. Verify coordinates
    logger.info("Rendering at: ${pos.x}, ${pos.y}, ${pos.z}")
    
  3. Check GL state - Ensure proper GL setup

Debugging Mixins

Mixin Not Applying

  1. Check mixin configuration in fabric.mod.json
  2. Verify target class
    @Mixin(ClientPlayerEntity.class)
    public class MixinClientPlayerEntity {
        // Ensure class name is correct
    }
    
  3. Check injection point
    @Inject(method = "tick", at = @At("HEAD"))
    private void onTick(CallbackInfo ci) {
        System.out.println("Mixin injected!");
    }
    
  4. Review Mixin logs - Check console for mixin application errors

Performance Debugging

Profiling

JVM Profiler

Use IntelliJ’s built-in profiler:
  1. Run → Profile ‘LiquidBounce [runClient]’
  2. Analyze CPU and memory usage
  3. Identify bottlenecks

Manual Timing

import kotlin.system.measureTimeMillis

val duration = measureTimeMillis {
    // Code to measure
    performExpensiveOperation()
}

logger.info("Operation took ${duration}ms")

Memory Leaks

Check for Retained References

  1. Take heap dump after running for a while
  2. Analyze with Memory Profiler
  3. Look for growing collections or unclosed resources

Common Issues

  • Event handlers not unregistered
  • Static collections growing unbounded
  • Cached data not cleared

Remote Debugging

Attach Debugger to Running Instance

  1. Add JVM arguments
    ./gradlew runClient -Dorg.gradle.jvmargs="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
    
  2. Create Remote Debug Configuration in IntelliJ
    • Run → Edit Configurations → + → Remote JVM Debug
    • Host: localhost
    • Port: 5005
  3. Attach debugger when client is running

Build Debugging

Gradle Build Issues

Enable Debug Logging

./gradlew build --debug > build.log 2>&1
Review build.log for detailed information.

Clean and Rebuild

./gradlew clean build

Check Dependency Resolution

./gradlew dependencies

Theme Build Issues

Manual Theme Build

cd src-theme
npm install
npm run build

Check Node.js Version

node --version
npm --version
Ensure you’re using a compatible version.

Testing and Validation

Run Tests with Debugging

./gradlew test --debug-jvm
Then attach debugger on port 5005.

Code Quality Checks

Run Detekt

./gradlew detekt
Fix any violations before debugging further.

Verify i18n

./gradlew verifyI18nJsonKeys

Common Issues and Solutions

Client Won’t Start

  1. Check JDK version
    java -version  # Should be 21 or higher
    
  2. Verify Gradle sync completed in IDE
  3. Regenerate sources
    ./gradlew cleanLoom genSources
    

ClassNotFoundException

  1. Rebuild project
    ./gradlew clean build
    
  2. Invalidate caches (IntelliJ: File → Invalidate Caches)
  3. Check dependencies in build.gradle.kts

Crashes

  1. Check crash log in crash-reports/ directory
  2. Look for stack trace in console
  3. Identify last executed code before crash
  4. Reproduce with minimal code to isolate issue

Debugging Tools

In-Game Commands

Use LiquidBounce commands for live debugging:
.help              # List all commands
.toggle <module>   # Toggle module for testing
.binds             # Show module bindings

External Tools

  • VisualVM: JVM monitoring and profiling
  • JProfiler: Advanced profiling
  • YourKit: Performance analysis
  • MAT (Memory Analyzer Tool): Heap dump analysis

Best Practices

  1. Use meaningful log messages - Include context
  2. Remove debug logs before committing
  3. Use proper log levels - Info, Debug, Warn, Error
  4. Test edge cases - Not just happy path
  5. Profile performance - Don’t guess, measure
  6. Write unit tests - Prevent regressions
  7. Use version control - Commit working states

Next Steps

Build docs developers (and LLMs) love