Lua Plugin System

Moonlight AI includes a sandboxed Lua scripting engine that lets you write custom plugins to override or extend any stage of the aim pipeline. Plugins are .lua files dropped into the plugins/ folder — no compilation, no restarts.


Quick Start

  1. Open the Plugins tab in the UI

  2. Click Open Folder to open the plugins/ directory

  3. Drop a .lua file in (or edit one of the included examples)

  4. Click Reload All — your plugin appears in the list

  5. Select it and check Enable

  6. Adjust any config sliders the plugin exposes

That's it. The plugin is now live in the aim loop.


How It Works

Each plugin gets its own isolated Lua runtime (powered by LuaJITarrow-up-right via lupaarrow-up-right). On every frame, Moonlight passes live aim data into your script and calls whichever hook functions you define. Your function receives the current values, does whatever math you want, and returns the modified result. Multiple plugins chain together — output from one feeds into the next.

Detection → [Filter Targets] → [Select Target] → [Aim Point]
         → [Prediction] → [Smoothing] → [Humanization] → [Output] → Mouse
                ↑               ↑             ↑               ↑
            Your plugins hook into any of these stages

Plugin Structure

Every plugin is a single .lua file with four required metadata fields, an optional config table, and one or more hook functions.

Minimal Example

Metadata Fields

Field
Required
Description

plugin_name

Yes

Display name shown in the UI

plugin_description

Yes

Short description of what it does

plugin_author

Yes

Author name

plugin_version

Yes

Version string (e.g. "1.0")


Config System

Plugins can expose sliders in the UI by defining a config table. Each entry automatically generates a slider with label, tooltip, min/max bounds, and a default value.

Defining Config

Reading Config Values

Inside any hook function, read the current slider value with a fallback:

Always provide an or fallback — config values sync from the UI asynchronously, and on the very first frame the value may not be populated yet.

Persistence

Config values are automatically saved to your active config file and restored on next launch. No extra code needed.


Hook Functions

Define any combination of these functions. You only need to implement the hooks you care about — unimplemented hooks are skipped with zero overhead.

override_filter_targets(targets)

When: After detection, before target selection. Purpose: Remove, reorder, or annotate the raw detection list.

override_select_target(targets, current_lock)

When: During target selection. Purpose: Override which target gets locked.

override_aim_point(target)

When: After target is selected. Purpose: Calculate the exact pixel to aim at on the target (e.g. custom bone targeting).

override_prediction(target, aim_x, aim_y)

When: After aim point is calculated. Purpose: Add lead/prediction compensation for moving targets.

override_smoothing(cur_x, cur_y, tgt_x, tgt_y, delta_ms)

When: During aim smoothing. Purpose: Replace the built-in smoothing algorithm with your own easing curve.

Parameter
Description

cur_x/y

Current crosshair position

tgt_x/y

Target position to move toward

delta_ms

Time since last frame in milliseconds

override_humanization(aim_x, aim_y)

When: After smoothing, before output. Purpose: Add human-like imperfection (tremor, jitter, micro-corrections).

override_output(delta_x, delta_y)

When: Final stage, right before the mouse move is sent. Purpose: Clamp, scale, or transform the final movement vector.


Aim State

Every frame, Moonlight pushes live data into the global aim table. This is read-only — use it to make context-aware decisions in your hooks.

Example: Using Aim State


Built-in Utility Functions

These are injected into every plugin's sandbox:

Function
Description

clamp(value, min, max)

Clamp a number to a range

lerp(a, b, t)

Linear interpolation between a and b

distance(x1, y1, x2, y2)

Euclidean distance between two points

normalize(x, y)

Normalize a 2D vector to unit length

log(message)

Print to the Moonlight log (rate-limited)

log() is rate-limited to 10 messages per second to prevent accidental log floods from per-frame logging.


Standard Lua Available

The full Lua math library is available:

String operations (string.len, string.sub, string.format, etc.) and table operations (table.insert, table.remove, table.sort, ipairs, pairs) are also available.


Sandbox Restrictions

For security, plugins run in a restricted environment. The following are blocked:

Blocked
Reason

os, io

No filesystem access

require, package

No loading external modules

loadfile, dofile

No executing external files

loadstring, load

No dynamic code generation

debug

No runtime introspection

rawget, rawset

No bypassing metatables

string.dump

No bytecode extraction

collectgarbage

No GC manipulation

An instruction limit of 1,000,000 per frame prevents infinite loops from freezing the aim loop. If your script exceeds this, it is automatically terminated for that frame.


Plugin Chaining

When multiple plugins are enabled, they execute in list order. Each hook chains — the output of one plugin becomes the input to the next.

Example with two smoothing plugins enabled:

Drag plugins in the UI list to reorder them. Place coarse adjustments first, fine adjustments last.


Error Handling

Plugins are fault-isolated:

  • If a hook function throws an error, it is caught and logged

  • After 3 consecutive errors, the failing hook is automatically disabled (the plugin stays loaded, other hooks still run)

  • A broken plugin never crashes Moonlight or affects other plugins

  • Errors are visible in the plugin info panel in the UI


Performance

  • Hook execution time is tracked internally and visible via get_hook_timings()

  • Each plugin runs in its own LuaJIT runtime — near-native speed

  • Hooks that aren't defined are skipped entirely (no overhead)

  • The instruction limit prevents runaway scripts from causing frame drops

For best performance:

  • Avoid calling log() every frame

  • Keep math simple in per-frame hooks

  • Use local variables (faster than global lookups in Lua)


File Structure

The plugins/ folder is created automatically on first launch. Drop .lua files in, click Reload All in the Plugins tab, and they appear immediately.


Full Example: Distance Smoothing

This is the included example_smoothing.lua — a complete working plugin that applies slower smoothing to close targets and faster smoothing to distant ones.

Full Example: Human Tremor

The included example_humanization.lua — simulates physiological hand tremor using layered sine waves plus random micro-jitter.

Last updated