Skip to content

Latest commit

 

History

History
438 lines (327 loc) · 11 KB

File metadata and controls

438 lines (327 loc) · 11 KB

Debug API Documentation

The Debug API provides Lua scripts with debugging capabilities, allowing them to log messages, check debug mode status, and trace execution context. This API is particularly useful for troubleshooting and development.

Global Access

The API is accessed via the global DebugApi table in Lua scripts.

Debug Mode

The debug mode is controlled by the debug-mode setting in the plugin's config.yml file. When debug mode is enabled, debug logs are shown in the console. When disabled, only warning and error logs are shown.

The standard debug library

Separately from DebugApi, Lua's own debug table is present in the Lua state whenever the execution watchdog is enabled, which is the default. The watchdog is implemented as the interpreter's debug hook, and installing it brings the standard library along with it. Setting script-timeout-ms: 0 disables the watchdog and debug is then absent, so a script that uses it should guard with if debug then ... end.

One caveat comes with that: the watchdog keeps the interpreter's per-instruction bookkeeping switched off while scripts are running normally, and only turns it on at the moment a script overruns its budget. A count or line hook installed by a script with debug.sethook therefore does not fire during normal execution. debug.traceback and the rest of the library work as usual.

Methods

log

Logs a debug message to the console. This message is only shown when debug mode is enabled.

Syntax:

DebugApi:log(message)

Parameters:

  • message (string): The message to log

Example:

DebugApi:log("Player joined the game")
DebugApi:log("Processing item: " .. itemName)

Output (when debug mode is enabled):

[INFO] [DEBUG][script.lua] Player joined the game
[INFO] [DEBUG][script.lua] Processing item: diamond

warn

Logs a warning message to the console. This message is always shown regardless of debug mode.

Syntax:

DebugApi:warn(message)

Parameters:

  • message (string): The warning message to log

Example:

DebugApi:warn("Player inventory is full")
DebugApi:warn("Invalid configuration value")

Output:

[WARNING] [DEBUG][script.lua] Player inventory is full
[WARNING] [DEBUG][script.lua] Invalid configuration value

error

Logs an error message to the console. This message is always shown regardless of debug mode.

Syntax:

DebugApi:error(message)

Parameters:

  • message (string): The error message to log

Example:

DebugApi:error("Failed to process player data")
DebugApi:error("Critical error in event handler")

Output:

[SEVERE] [DEBUG][script.lua] Failed to process player data
[SEVERE] [DEBUG][script.lua] Critical error in event handler

isEnabled

Checks if debug mode is currently enabled.

Syntax:

local enabled = DebugApi:isEnabled()

Returns:

  • enabled (boolean): true if debug mode is enabled, false otherwise

Example:

if DebugApi:isEnabled() then
    DebugApi:log("Debug information: " .. data)
end

trace

Logs the current script execution context to the console. This is useful for tracking which script is currently being executed. This message is only shown when debug mode is enabled.

Syntax:

DebugApi:trace()

Example:

DebugApi:trace()

Output (when debug mode is enabled):

[INFO] [DEBUG][TRACE] Current script: myscript.lua

Output (when no script context):

[INFO] [DEBUG][TRACE] No current script context

startTimer

Starts a named timer for performance measurement. This is useful for measuring execution time of code blocks. This only works when debug mode is enabled.

Syntax:

DebugApi:startTimer(name)

Parameters:

  • name (string): The name of the timer

Example:

DebugApi:startTimer("database_query")
-- perform database query
DebugApi:stopTimer("database_query")

Output (when debug mode is enabled):

[INFO] [DEBUG][TIMER][script.lua] Started timer: database_query
[INFO] [DEBUG][TIMER][script.lua] Stopped timer 'database_query': 42ms

stopTimer

Stops a named timer and logs the elapsed time in milliseconds. Returns the elapsed time as a number. This only works when debug mode is enabled.

Syntax:

local elapsedMs = DebugApi:stopTimer(name)

Parameters:

  • name (string): The name of the timer to stop

Returns:

  • elapsedMs (number): The elapsed time in milliseconds, or nil if timer not found

Example:

DebugApi:startTimer("processing")
-- do some work
local time = DebugApi:stopTimer("processing")
print("Operation took " .. time .. "ms")

Output (when debug mode is enabled):

[INFO] [DEBUG][TIMER][script.lua] Stopped timer 'processing': 123ms

Output (if timer not found):

[WARNING] [DEBUG][TIMER] Timer not found: processing

Note: Always call stopTimer in a finally block or use clearTimers to prevent memory leaks if the timer might not be stopped due to errors.


clearTimers

Clears all active timers. This is useful for cleaning up timers that were started but not stopped due to errors or early returns. This only works when debug mode is enabled.

Syntax:

DebugApi:clearTimers()

Example:

function riskyOperation()
    DebugApi:startTimer("operation")
    local success, err = pcall(function()
        -- risky code that might error
    end)
    
    if not success then
        DebugApi:error("Operation failed: " .. err)
        DebugApi:clearTimers() -- Clean up timers on error
        return
    end
    
    DebugApi:stopTimer("operation")
end

Output (when debug mode is enabled):

[INFO] [DEBUG][TIMER][script.lua] Cleared 3 timer(s)

Note: This method removes all timers from the timer map, preventing memory leaks from unreleased timers.


printTable

Pretty-prints a Lua table to the console for debugging. This is useful for inspecting table contents and structure. This only works when debug mode is enabled.

Syntax:

DebugApi:printTable(table)

Parameters:

  • table (table): The Lua table to print

Example:

local playerData = {
    name = "Steve",
    health = 20,
    inventory = {"diamond", "iron"}
}
DebugApi:printTable(playerData)

Output (when debug mode is enabled):

[INFO] [DEBUG][TABLE][script.lua] {name = "Steve", health = 20, inventory = {1 = "diamond", 2 = "iron"}}

Output (if table is truncated):

[INFO] [DEBUG][TABLE][script.lua] {key1 = "value1", key2 = "value2", ... (truncated)}

Notes:

  • Nested tables are recursively printed up to a maximum depth
  • Strings are shown with quotes
  • Numbers are shown without quotes
  • nil values are shown as nil
  • Functions are shown as [function]
  • Circular references are detected and rendered as [table: circular reference]
  • A maximum depth of 10 is enforced; deeper tables render as [table: max depth reached]
  • A shared rendering budget of 100 entries is enforced across the entire recursive call tree to prevent excessive log output
  • The budget is not reset for nested tables or sibling branches
  • When the budget is exhausted, the output appends , ... (truncated)

Usage Examples

Basic Debugging

-- Check if debug mode is enabled before expensive operations
if DebugApi:isEnabled() then
    DebugApi:log("Starting complex calculation")
    local result = complexCalculation()
    DebugApi:log("Result: " .. result)
end

Conditional Logging

-- Log debug information only when needed
function processItem(item)
    if item == nil then
        DebugApi:error("Item is nil")
        return
    end

    if DebugApi:isEnabled() then
        DebugApi:log("Processing item type: " .. item:getType())
    end

    -- Process the item
end

Error Handling

function safeOperation()
    local success, err = pcall(function()
        -- risky operation
    end)

    if not success then
        DebugApi:error("Operation failed: " .. err)
    end
end

pcall catches script errors, but deliberately not the abort raised when a script exceeds script-timeout-ms: a script looping on pcall would otherwise swallow the abort and keep the server hung. Cleanup placed in the failure branch above does not run when a script is aborted for running too long, so do not rely on pcall to release state that must be released no matter what.

Tracking Execution Flow

function onPlayerJoin(event)
    DebugApi:trace()
    DebugApi:log("Player joined: " .. event:getPlayer():getName())

    -- Handle player join
end

Performance Measurement

function processPlayerData(player)
    DebugApi:startTimer("data_processing")

    -- Expensive operation
    local data = fetchPlayerData(player)
    processData(data)

    local elapsed = DebugApi:stopTimer("data_processing")
    if elapsed and elapsed > 100 then
        DebugApi:warn("Slow data processing: " .. elapsed .. "ms")
    end
end

Safe Timer Usage

function safeTimedOperation()
    DebugApi:startTimer("operation")
    local success, err = pcall(function()
        -- operation that might fail
    end)
    
    if not success then
        DebugApi:error("Operation failed: " .. err)
        DebugApi:clearTimers() -- Clean up on error
        return
    end
    
    DebugApi:stopTimer("operation")
end

Table Inspection

function debugPlayerInventory(player)
    local inv = player:getInventory()
    local inventory = {}
    for i = 1, inv:getSize() do
        local item = inv:getItem(i - 1)
        if item ~= nil then
            inventory[i] = item:getType()
        end
    end

    DebugApi:log("Player inventory contents:")
    DebugApi:printTable(inventory)
end

Combined Debugging

function handleEvent(event)
    if not DebugApi:isEnabled() then
        -- Fast path when debug mode is off
        return processEvent(event)
    end

    DebugApi:trace()
    DebugApi:startTimer("event_handling")

    local eventData = {
        type = event:getEventName(),
        player = event:getPlayer():getName()
    }
    DebugApi:printTable(eventData)

    local result = processEvent(event)

    DebugApi:stopTimer("event_handling")
    return result
end

Notes

  • All log messages include the script name in the format [DEBUG][script_name] when called from within a script context
  • The log, trace, startTimer, stopTimer, clearTimers, and printTable methods only output when debug mode is enabled
  • The warn and error methods always output regardless of debug mode
  • Debug mode can be toggled at runtime using the /minecraftluascripting reloadconfig command
  • Timers are stored per DebugApi instance in a ConcurrentHashMap, which is thread-safe
  • Always call stopTimer or use clearTimers to prevent memory leaks from unreleased timers
  • printTable recursively prints nested tables up to a maximum depth and detects circular references
  • printTable enforces a shared rendering budget of 100 entries across the entire recursive call tree to prevent excessive log output