getvexor / docs / luavm

Docs / Lua VM content written by ai

A. What this VM is (and is not)

Full handbook for the overlay Lua VM. Read it like a tutorial the first time. After that, jump the table of contents. Every function the VM exposes is listed. If it is missing from here, it is not part of the public script API.

Host
vexor-menu-1
Language
Lua 5.4 in Vexor
Not
Roblox Lua / Luau
Scripts
one running at a time

This is Lua 5.4 running inside Vexor, the overlay process on your PC. It is not Roblox Lua. It is not Luau. It is not injected into RobloxPlayerBeta. You cannot write:

game.Players.LocalPlayer.Character.HumanoidRootPart.CFrame = ...
Instance.new("Part")
workspace:FindFirstChild("x")

Those APIs do not exist here. There is no game global in the Roblox sense.

What you do have is the same toolkit the C++ cheat already uses:

  1. Read and write Roblox process memory (vexor.mem, vexor.part, vexor.player)
  2. The live player/entity cache (vexor.players.list)
  3. VisualEngine world-to-screen (vexor.world.to_screen)
  4. Overlay drawing on the transparent window (vexor.draw / vexor.esp)
  5. ImGui windows, sliders, colour pickers (vexor.gui)
  6. Offsets, place detection, PF camera look-at, MM2 roles
Roblox process  <--memory driver-->  Vexor C++  <--Lua C API-->  your script
                                      |
                                      +-- overlay HWND (draw + GUI)

If you can imagine a feature as “read positions, project to screen, draw boxes, maybe write a position” you can write it here. That is how every overlay cheat works. The VM just lets you do it in Lua instead of C++.

vexor.host is the string "vexor-menu-1". If you ever see a script calling vexor.exec.inject, that path was removed. This host only runs in the menu.

B. First 60 seconds in the IDE

  1. Attach Vexor to Roblox as usual.
  2. Click the code icon on the overlay top bar. The Lua VM frame opens.
  3. Type in the big editor. Tabs along the top are separate buffers. Click a tab name to rename it. + adds a tab. x closes one.
  4. Bottom-right of the frame, three buttons:
ButtonWhat it does
ClearEmpties the console under the editor. Does not stop a running script.
TerminateStops the running script. Clears overlay drawings and Lua GUI windows. Restores journaled vexor.cfg / cheats / FOV. If the script ignores the stop hook for ~2 seconds, the VM is hard-reset.
ExecuteCompiles the active tab and runs it on a background thread. Only one script can run at a time.

The console under the editor is where print(), errors, and tracebacks go.

ColourTagSource
Grey[SYS]engine messages
White[INFO]print, log, “Executing…”
Yellow[WARN]vexor.warn, “already running”
Red[ERR]compile errors, runtime errors, assert

The IDE still takes mouse when the main Vexor menu is closed, as long as the cursor is over the Lua VM frame (or over a Lua GUI window you created).

Click Execute. You should see:

Executing "yourscript.lua" (N bytes) ...

If compile fails, you get a red line with a line number. That number is the Lua source line in the editor, not a C++ line.

Paste this first. If you see the text and a small window, the VM is live:

print("lua vm ok", vexor.host, vexor.game.place_name())
vexor.debug.info()

while true do
    vexor.draw.clear()
    local w, h = vexor.world.screen_size()
    vexor.draw.text_ex(20, 20, "Vexor Lua VM", 255,255,255,255, 18, "ui", 1)
    vexor.draw.crosshair(w * 0.5, h * 0.5, 8, 255,255,255,180, 1)
    vexor.gui.begin("Lua VM", 40, 80)
    vexor.gui.text("drag this window — input capture works")
    local r,g,b,a = vexor.gui.color("c", "Accent", 120, 180, 255, 255)
    vexor.gui.colored_text("picker live", r, g, b, a)
    vexor.gui.finish()
    vexor.sleep(1)
end

Then press Terminate. Draws and the window disappear. That is the full cycle.

C. The one loop you must always write

Scripts that draw or show GUI must loop. A script that runs to the end and returns will flash one frame (or nothing) and then the VM clears everything.

while true do
    vexor.draw.clear()          -- start a new overlay batch
    -- queue draw calls
    -- declare gui widgets
    vexor.sleep(1)              -- PUBLISH overlay + GUI, then wait 1 ms
end
CallWhat it does
vexor.draw.clear()Throw away overlay commands queued since the last present. The last published frame stays on screen until the next present. Call this at the top of every tick so you do not stack 8000 lines.
vexor.sleep(ms)Publishes the overlay batch, publishes the GUI widget tree, then sleeps ms milliseconds. ms = 0 still publishes, then returns immediately. 1 ms is the normal overlay-frame rate.
vexor.present()Same publish as sleep, but no wait. Use only if you also sleep yourself. Prefer vexor.sleep(1).
vexor.draw.present()Overlay only. Does not publish GUI. Almost always wrong if you also have vexor.gui windows.
vexor.gui.present()GUI only. Same warning.
vexor.draw.clear_all()Wipes pending and the last published overlay. The screen goes blank until you present again.
vexor.stop()Raises the same kill error Terminate uses. Script-side off switch.
x
Do not busy-spin

A while true with no sleep never presents (or presents so rarely the overlay looks frozen), and Terminate has to wait for the line hook. Always sleep. A one-shot draw without a loop is also useless: the script ends and the overlay is cleared.

D. How drawing actually appears on screen

Your Lua thread cannot touch DirectX. It queues commands into a lock-free batch. The render thread paints the last published batch every overlay frame.

Lua tick N:    clear, line, text, rect, gui.begin...gui.finish, sleep(1)
               sleep presents batch N
Render thread: paints batch N on the overlay (behind Vexor menu windows)
Lua tick N+1:  clear, ... sleep presents batch N+1
Render thread: paints batch N+1

Coordinates are overlay-client pixels. (0,0) is the top-left of the overlay window, which covers the game. vexor.world.screen_size() is the VisualEngine resolution, which is what world-to-screen uses. For HUD you usually want those same dimensions.

Draw order: first command is underneath later commands in the same batch. Lua overlay is drawn on the background draw list so your vexor.gui windows sit on top of ESP boxes. The main Vexor menu still sits above that.

8000 overlay commands per published frame. Extra commands are dropped. vexor.draw.dropped() returns how many were discarded this session. If ESP flickers or parts go missing on busy servers, you hit the cap — draw less (skip off-screen, skip dead, skip NPCs).

E. How GUI windows actually take clicks

vexor.gui is ImGui, but Lua does not run on the render thread. The pattern is “declare every tick, values live in named slots”.

vexor.gui.begin("ESP", 60, 80)          -- title, x, y  (w,h optional)
local on = vexor.gui.checkbox("box", "Boxes", true)
local r,g,b,a = vexor.gui.color("col", "Colour", 255, 80, 80, 255)
if vexor.gui.button("tp", "Teleport") then
    -- true the tick AFTER the user clicked
    vexor.player.set_pos(0, 50, 0)
end
vexor.gui.finish()
  1. The first argument of almost every widget is a string id. It must stay the same every frame or the widget forgets its value. Bad: vexor.gui.checkbox("box"..i, ...) with a new i every tick. Good: vexor.gui.checkbox("box", "Boxes", true).
  2. Default values (the last args) apply only the first time that id is seen. After that, the user’s clicks/drags own the value.
  3. Buttons are one-frame delayed. Click → render thread stores clicked → next Lua loop returns true → you consume it (it will not stay true).
  4. You must call begin and finish every tick you want the window visible. Skip a tick and the window vanishes.
  5. w,h = 0 (or omitted height) means auto-resize to content.
  6. pinPosBool (6th arg of begin) true = force x,y every frame (cannot drag). false/omitted = SetNextWindowPos once.
  7. When the main menu is closed, the overlay is click-through except: cursor over the Lua VM IDE, cursor over a Lua GUI window, or you are dragging a Lua slider/picker. If clicks fall through to the game, move the cursor onto the window.
  8. Colour widgets return 0–255, same as draw calls. Keep them in locals and pass those locals into vexor.draw.* that same tick.

F. Lua 5.4 rules that will bite you

This is standard Lua 5.4, not Luau.

1. Reserved words cannot be field names with a dot

p.local          -- ILLEGAL   error: <name> expected near 'local'
p["local"]       -- legal
p.is_local       -- legal (alias we also set)

Lua reserved words: and break do else elseif end false for function goto if in local nil not or repeat return then true until while. Never write p.end, p.function, p.repeat. Use p["end"] if a field is ever named that. The “is this you?” flag is is_local / ["local"] for this reason.

2. Arrays are 1-based

names = { "a", "b", "c" }
names[1] == "a"
vexor.gui.combo returns 1, 2, 3 — not 0, 1, 2.

3. Multiple returns and nil

If the point is behind the camera, to_screen returns a single nil. Then sx is nil and sy is nil. Always check:

local sx, sy = vexor.world.to_screen(x, y, z)
if sx then
    vexor.draw.circle(sx, sy, 4, 255,255,255,255)
end

Same for vexor.players.screen_box (four numbers or one nil) and vexor.players.local_player() (table or nil).

4–10

G. How to read the console / debug anything

Compile error

Red line, no traceback, points at a line. <name> expected near 'local' → you wrote .local or a broken string so the parser hit a keyword. unexpected symbol near X → extra comma, missing end, bad quote. Fix the line. Execute again. Nothing is “stuck”.

Runtime error

Red lines with a traceback (file:line in function). “HumanoidRootPart not available” → character not loaded yet. Guard with if vexor.player.hrp_address() ~= 0. “attempt to index a nil value” → you did p.parts.Head without checking p, p.parts, or that Head exists. Wrap risky calls in pcall, or nil-check every step.

Print like a debugger

CallUse
print(a, b, c)one-level tables are expanded
vexor.debug.dump(p)nested tables, also returns the string
vexor.debug.inspect(p.address)name, hp, every part address
vexor.debug.hex(addr, 64)classic hexdump into the console
vexor.debug.info()pid, module base, datamodel, camera, ve
vexor.debug.traceback()stack at that moment
vexor.debug.type(x)"table", "number", …
vexor.debug.assert(cond, "msg")errors if cond is false
vexor.log / vexor.warn / vexor.notifylog, warn, overlay toast

When you have no idea why ESP is empty:

print("alive", vexor.game.is_alive(), "place", vexor.game.place_name())
print("players", #vexor.players.list())
local me = vexor.players.local_player()
vexor.debug.dump(me)
local x,y,z = vexor.player.get_pos()
print("pos", x, y, z)
local sx,sy = vexor.world.to_screen(x,y,z)
print("w2s", sx, sy)

If players is 0, the C++ cache has not populated yet (lobby, loading, or not attached). Wait in the loop; do not error.

H. How to handle nil, missing players, and attach state

Always write defensive Lua. The cache rebuilds. People leave. You die. HumanoidRootPart vanishes for a frame.

Safe player loop:

local me = vexor.players.local_player()
local list = vexor.players.list()
for i = 1, #list do
    local p = list[i]
    if p and not p["local"] and p.address then
        local pos = p.position
        if pos then
            local sx, sy = vexor.world.to_screen(pos.x, pos.y, pos.z)
            if sx then
                -- draw
            end
        end
    end
end

Safe part:

local head = p.parts and p.parts.Head
if head and head.on_screen then
    vexor.draw.circle(head.sx2, head.sy2, 4, 255,255,255,255)
end

Safe local character:

local hrp = vexor.player.hrp_address()
if hrp ~= 0 then
    local x, y, z = vexor.player.get_pos()
    vexor.player.set_velocity(0, 0, 0)
end

Attach:

if not vexor.mem.alive() then
    vexor.draw.text(20, 20, "waiting for roblox", 255,80,80,255)
    vexor.sleep(16)
    -- continue loop, do not call set_pos
end

I. Colours

Draw calls take r,g,b,a as 0–255 integers after the geometry. Alpha defaults to 255 if you omit it.

vexor.draw.line(x1, y1, x2, y2, r, g, b, a, thickness)

Some calls have extra numbers after rgba (thickness, rounding, segments). Count carefully. For line, thickness is argument 9 because rgba occupies 5–8.

GUI colour widgets also speak 0–255:

local r,g,b,a = vexor.gui.color("box", "Box colour", 255, 80, 80, 255)
HelperReturns
vexor.color.pack(r,g,b,a)ImU32 integer
vexor.color.unpack(packed)r,g,b,a
vexor.color.lerp(r1,g1,b1,a1, r2,g2,b2,a2, t)t 0–1
vexor.color.luminance(r,g,b)luminance
vexor.color.invert_rgb(r,g,b)r,g,b
vexor.color.hsv_to_rgb255(h,s,v)h 0–360, s/v 0–1
vexor.color.lighten / darken(r,g,b, t)r,g,b
vexor.color.with_alpha(r,g,b,a)u32

Team colour pattern:

local r, g, b = 255, 80, 80
if me and p.team ~= 0 and p.team == me.team then
    r, g, b = 80, 180, 255
end

J. Fonts and text

Font names (string) or ids (number):

Id / nameFont
0 / "default"ImGui default
1 / "ui"menu body (Inter)
2 / "code"Consolas (same as the Lua editor)
3 / "esp"current ESP font (user can import a font in visuals)
4 / "qilka"qilka
5 / "tahoma"tahoma

vexor.fonts.list(){ "default","ui","code","esp","qilka","tahoma" }

vexor.draw.text(x, y, "hello", r,g,b,a)
vexor.draw.text(x, y, "hello", r,g,b,a, size, font, outline, or,og,ob,oa)
vexor.draw.text_ex(x, y, "hello", r,g,b,a, size, font, outline, or,og,ob,oa)
w, h = vexor.draw.measure("hello", size, font)
vexor.draw.text_ex(20, 40, "ESP", 255,255,255,255, 16, "esp", 1, 0,0,0,220)
vexor.draw.text_ex(20, 64, "dump", 180,255,180,255, 14, "code", 0)

local label = p.display_name
local tw, th = vexor.draw.measure(label, 13, "esp")
vexor.draw.text_ex(box_x + (box_w - tw) * 0.5, box_y - th - 2, label, 255,255,255,255, 13, "esp", 1)

K. Overlay drawing API

vexor.draw and vexor.overlay are the same table. Use either name. Rebuild every tick. Coordinates are overlay pixels.

CallNotes
line(x1,y1,x2,y2, r,g,b,a [, thickness])
line_dashed(..., thickness, dash, gap)
rect / rect_filled(x,y,w,h, r,g,b,a [, rounding])
rect_gradient(...)four corners, 4 channels each, order TL, TR, BR, BL
circle / circle_filled(cx,cy,radius, r,g,b,a [, segments, thickness])
polyline({ {x,y}, ... }, r,g,b,a [, thickness, closedBool])
convex_fill({ {x,y}, ... }, r,g,b,a)
triangle / triangle_filled / quad / quad_filled
ngon / ngon_filled(cx,cy,radius, r,g,b,a, segments, thickness)
bezier_cubic(x1,y1, x2,y2, x3,y3, x4,y4, r,g,b,a, thickness [, segments])
crosshair(cx,cy,size, r,g,b,a [, thickness])
grid(x,y,w,h, cols, rows, r,g,b,a [, thickness])
clip_push(x,y,w,h [, intersectBool]) / clip_pop()
clear / clear_all / present / dropped
text / text_ex / measuresee Fonts

ESP helpers. These are only drawing. They do not enable Vexor’s built-in ESP.

CallNotes
vexor.esp.box(x,y,w,h, r,g,b,a [, thickness])
vexor.esp.filled(x,y,w,h, r,g,b,a)
vexor.esp.corner(x,y,w,h, r,g,b,a [, thickness])
vexor.esp.healthbar(x, y, bar_w, bar_h, hp, maxhp)vertical bar, green→red by fraction, black backing
vexor.esp.tracer(x, y, r,g,b,a [, thickness])line from bottom-center of the screen to (x,y)
vexor.esp.skeleton({ {x1,y1}, {x2,y2}, ... }, r,g,b,a [, th])consecutive pairs of points are line segments
vexor.esp.r15_bones(){ {"Head","UpperTorso"}, {"UpperTorso","LowerTorso"}, ... }
vexor.esp.r6_bones(){ {"Head","Torso"}, {"Torso","Left Arm"}, ... }
local bones = vexor.esp.r15_bones()
local segs = {}
for i = 1, #bones do
    local a = p.parts[bones[i][1]]
    local b = p.parts[bones[i][2]]
    if a and b and a.on_screen and b.on_screen then
        segs[#segs+1] = { a.sx2, a.sy2 }
        segs[#segs+1] = { b.sx2, b.sy2 }
    end
end
if #segs > 0 then
    vexor.esp.skeleton(segs, 255,255,255,180, 1)
end

If the rig is R6, Head/Torso exist and UpperTorso does not — use r6_bones(). You can try r15 first and fall back if segs is empty.

L. World-to-screen and camera

CallNotes
sx, sy = vexor.world.to_screen(x, y, z)nil if behind camera or VisualEngine is missing
ok = vexor.world.on_screen(sx, sy [, margin])takes screen pixels, not world. margin extra pixels outside
w, h = vexor.world.screen_size()VisualEngine dimensions (HUD and tracers)
dist = vexor.world.distance3(x1,y1,z1, x2,y2,z2)
fx,fy,fz, rx,ry,rz, ux,uy,uz = vexor.world.camera_axes()forward, right, up of the camera rotation matrix
x1,y1, x2,y2, ok1, ok2 = vexor.world.line_to_screen(...)projects two world points; ok flags say if each landed

Camera writes the live camera instance, same as C++:

CallNotes
vexor.camera.get_pos / set_pos(x,y,z)
vexor.camera.get_fov / set_fov(fov)
get_rotation_matrix / set_rotation_matrix(m00..m22)9 numbers, row-major 3×3
get_subject / set_subject(addr)
get_type / set_type(t)0 fixed, 1 attach, 2 watch, 3 track, 4 follow, 5 custom, 6 orbital, 7 scriptable
local w, h = vexor.world.screen_size()
local sx, sy = vexor.world.to_screen(wx, wy, wz)
if not sx then
    -- behind you: skip
elseif not vexor.world.on_screen(sx, sy, 8) then
    local cx, cy = w * 0.5, h * 0.5
    -- projected but outside the monitor: clamp for arrows
else
    vexor.draw.circle(sx, sy, 3, 255,255,255,255)
end

M. Local player (vexor.player)

These helpers talk to your character. They error if HumanoidRootPart is missing — wrap in a check. get_pos returns 0,0,0 if missing (no error).

CallNotes
get_pos / set_pos / teleport(x,y,z)set_pos is the same as teleport
set_velocity(x,y,z)
hrp_address()0 if missing
child_address("Head") / child_primitive("Head") / child_pos / set_child_pos
child_velocity("HumanoidRootPart")
list_children()names
humanoid_health / humanoid_max_health
get_walkspeed / set_walkspeed(n)
-- soft freeze (hold F)
if vexor.input.key_down(0x46) then
    vexor.player.set_velocity(0, 0, 0)
end

-- hop pulse (SPACE)
if vexor.input.key_pressed(0x20) then
    vexor.player.set_velocity(0, 70, 0)
end

N. All players — write ESP / aim from scratch

Data comes from the same entity cache the C++ ESP uses. It is a snapshot, typically tens of milliseconds old. That is normal.

CallNotes
vexor.players.list()array of player tables
vexor.players.local_player()nil if not resolved
vexor.players.get(address)nil if they left the cache
set_pos / set_velocity(address, x,y,z)writes that player’s HRP
part(address, "Head")instance address or nil
set_walkspeed / walkspeed / set_jump_height
screen_box(address)axis-aligned screen box of all parts, or nil if none project

Player table fields

name, display_name, user_id, address (player instance — use this as the id), team (0 if none), health, max_health, armor, distance (studs vs local), country, gender, tool, tool_address, platform, os, rig_type, humanoid_state, humanoid, mm2_role ("Murderer" / "Sheriff" / "Innocent" / ""), is_local (also p["local"]), position { x, y, z } from HRP (may be 0s if no HRP), parts map keyed by part name:

p.parts.Head = {
    address, primitive,
    x, y, z,            -- world
    sx, sy, sz,         -- size
    vx, vy, vz,         -- velocity
    on_screen,          -- bool
    sx2, sy2            -- screen of the origin (not a box)
}

Lighter tables from vexor.game.get_players() / vexor.game.local_player(): name, display_name, user_id, address, team, health, max_health, distance, mm2_role, humanoid, position (no parts map).

How to handle “is this me?”:

-- WRONG:  if not p.local then          -- Lua keyword, will not compile
-- RIGHT:  if not p["local"] then
-- RIGHT:  if not p.is_local then       -- after current builds
local function closest(me, list)
    if not me or not me.position then return nil, 1e9 end
    local best, best_d = nil, 1e9
    for i = 1, #list do
        local p = list[i]
        if p and not p["local"] and p.position then
            local d = vexor.world.distance3(
                me.position.x, me.position.y, me.position.z,
                p.position.x, p.position.y, p.position.z)
            if d < best_d then
                best, best_d = p, d
            end
        end
    end
    return best, best_d
end

O. Instances / DataModel tree

Everything in Roblox is an instance with an address. You walk it like a tree. Addresses are Lua integers, or hex strings "0xDEADBEEF". Both work anywhere we take an address.

CallNotes
vexor.inst.name(addr)
vexor.inst.class_name(addr)
vexor.inst.parent(addr)
vexor.inst.find(addr, "HumanoidRootPart")0 if missing
vexor.inst.find_class(addr, "Humanoid")
vexor.inst.children(addr)array of { address, name, class }
vexor.inst.set_parent(addr, parent_addr)

Root pointers: vexor.game.datamodel_address(), workspace_address(), camera_address(), visualengine_address(), local_player_address().

Dump Workspace (first 40) to understand a game:

local kids = vexor.inst.children(vexor.game.workspace_address())
local n = math.min(#kids, 40)
for i = 1, n do
    print(i, kids[i].class, kids[i].name, kids[i].address)
end

Then filter by class_name == "Part" / "MeshPart" / "Model", take part.pos, world-to-screen, draw. That is world ESP from scratch.

P. Parts and primitives

A visible part in memory is an instance + a Primitive (physics). Most position writes go to the primitive. Pass a BasePart/MeshPart instance address:

CallNotes
vexor.part.primitive(part_addr)
pos / set_pos / velocity / set_velocitypos is nil if no primitive
size / set_size
rotation / set_rotation9 numbers
transparency(part_addr)
color(part_addr)r,g,b 0–1 engine colour
prim_pos / prim_set_pos(prim_addr, x,y,z)when you already have p.parts.Head.primitive
vexor.humanoid.walkspeed / set_walkspeed / healthfrom p.humanoid or inst.find_class

Q. Memory — same path as the external

Addresses: number or "0xDEADBEEF". Writes use the same driver as C++.

When to use mem vs helpers: need player pos quickly → vexor.player.set_pos / players.set_pos. Need a field we did not wrap → vexor.offsets.get + vexor.mem.write_*. Research unknown memory → hexdump, read_bytes, chain.

Reads

read_float / read_double / read_int8 / read_uint8 / read_int16 / read_uint16 / read_int32 / read_uint32 / read_int64 / read_uint64 / read_bool

Writes

write_float / write_double / write_int8 / write_uint8 / write_int16 / write_uint16 / write_int32 / write_uint32 / write_int64 / write_uint64 / write_bool / write_ptr / write_vec3 / write_bytes / write_string(addr, str [, nullTerminateBool])

Process

vexor.mem.base() Roblox module base, pid(), module_size(), alive() false if Roblox closed.

Offsets are generated constants, not hardcoded features: vexor.offsets.Primitive.Position, vexor.offsets.get("Primitive.Position"). Explore with for k,v in pairs(vexor.offsets) do print(k,v) end.

local p = vexor.players.local_player()
if p and p.parts and p.parts.HumanoidRootPart then
    local prim = p.parts.HumanoidRootPart.primitive
    local off = vexor.offsets.get("Primitive.Position")
    vexor.mem.write_vec3(prim + off, 0, 50, 0)
end

if vexor.mem.valid(addr) then
    print(vexor.mem.hexdump(addr, 64))
else
    print("not readable", addr)
end
!
Bad writes

Do not write random addresses. A bad write can crash Roblox, not Vexor.

R. Game / place helpers

CallNotes
game_id / place_id / live_game_id / live_place_idlive_* re-read from DataModel
is_alive / place_name"phantom_forces" | "mm2" | "rivals" | "arsenal" | "unknown"
is_phantom_forces / is_mm2 / is_rivals / is_arsenal
mm2_role(player_address)"Murderer"|"Sheriff"|"Innocent"|""
job_id / server_ip / creator_id

Phantom Forces camera LookAt (same primitive C++ silent uses). This is a camera write, not a packaged aimbot. You pick the world point:

vexor.game.pf_silent(true, worldX, worldY, worldZ)
vexor.game.pf_silent(false)

if vexor.game.is_mm2() then
    local role = vexor.game.mm2_role(p.address)
    -- also p.mm2_role on the entity table
end

S. Optional built-in cheats / cfg

You do not need these. From-scratch scripts should draw and write memory themselves. These talk to the compiled C++ toggles if you want a hybrid.

vexor.cheats.set_aimbot(bool)
vexor.cheats.set_esp(bool)
vexor.cheats.set_noclip(bool)
vexor.cheats.set_speed(number)
vexor.cheats.set_fov(number)
v = vexor.cheats.get_setting("key")
vexor.cheats.set_setting("key", v)

v = vexor.cfg("visuals.box")             -- get
vexor.cfg("visuals.box", true)           -- set
list = vexor.list_cfg()                  -- every registered path
n, note = vexor.api_count()

Journaled settings are restored when you Terminate. If you only use vexor.draw, Terminate does not need to undo anything except your overlay/GUI. If you flip cfg/cheats, Terminate puts them back.

T. Interactive GUI API

vexor.gui.begin(title, x, y, w, h [, pinPosBool [, extraImGuiFlags]])
-- widgets
vexor.gui.finish()          -- alias: end_window()

Widgets (id string first):

CallNotes
checkbox(id, label [, defaultBool])
slider / slider_int(id, label, min, max [, default])
drag_float(id, label, speed, min, max [, default])
color / color_edit / color_picker(id, label [, r,g,b,a])returns r,g,b,a
button / small_button(id, label [, w, h])
input(id, label [, default])text
combo(id, label, {"A","B","C"}, defaultIndex)1-based
radio(id, label, value [, current])
selectable(id, label [, selected])
hotkey(id, label [, defaultVk])click, press a key; Esc cancels; returns VK
collapsing / tree / tree_pop

Layout: text, colored_text(str, r,g,b [,a]), bullet, label, separator, same_line([offset, spacing]), spacing, new_line, dummy(w, h), progress(fraction [, w, h, overlayText]), tooltip(str) (attaches to the previous widget), push_width / pop_width, begin_child / end_child, tab_bar / tab_item / end_tab_bar, hovered(id), present().

Two windows in one tick is fine. Call begin/finish twice with different titles. Keep ids unique across the whole script (“box” in two windows will share one checkbox state).

U. Keyboard and mouse

Virtual-key numbers are Win32 VK_*:

VKKey
0x01 / 0x02 / 0x04LBUTTON / RBUTTON / MBUTTON
0x08 / 0x09 / 0x0DBACK / TAB / RETURN
0x10 / 0x11 / 0x12SHIFT / CONTROL / MENU (Alt)
0x1B / 0x20ESCAPE / SPACE
0x25–0x28LEFT / UP / RIGHT / DOWN
0x30–0x390–9
0x41–0x5AA–Z
0x70–0x7BF1–F12
CallNotes
key_down / key_pressed / key_released(vk)held this overlay frame / edge down / edge up
mouse_pos / mouse_delta / display_size
mouse_down / mouse_clicked / mouse_released(button)0 left, 1 right, 2 middle
mouse_wheel / mod_ctrl / mod_shift / mod_alt
point_in_rect(x,y, rx,ry,rw,rh)

vexor.bind — named VK slots from lua_api_extensions (optional helper). For a hold-to-freeze feature, key_down is the right call. For a one-shot teleport, key_pressed (or a gui.button / gui.hotkey).

V. Math, time, files

vexor.math (also usable next to Lua’s own math.*): abs acos asin atan atan2 ceil clamp cos cosh cross cbrt deg dist2 dist2_sq dist3 dist3_sq dot2 dot3 angle_between2 exp floor fmod fract hash2 inv_lerp len3 lerp log log10 max min noise2 norm3 pi pingpong pow rad reflect3 remap round saturate sign sin sinh smoothstep smootherstep sqrt step tan tanh tau wrap.

vexor.vec / vexor.ease / vexor.util — extra helpers (easing, vector sugar). vexor.clock() seconds (GetTickCount64 / 1000). vexor.now_ms().

Files are sandboxed to %APPDATA%\Vexor\scripts (see vexor.scripts_dir): vexor.loadfile("rel.lua") / loadfile, vexor.dofile("rel.lua") / dofile, vexor.exec.status(). vexor.exec.inject / inject_file were removed on this host.

Example scripts in the repo (copy into the IDE, they are not auto-loaded):

W. What you can rebuild from scratch

You do not flip Vexor ESP and call it a day. You have the primitives to recreate (and invent) features:

Built-in C++ visuals still run independently. Turn them off if you only want Lua drawings.

X. Limits, sandbox, Terminate

Limits

Sandbox

Terminate

IDE button, or vexor.stop() in script. Cooperative: debug hook raises __vexor_kill__ on the next Lua line. If the script is stuck in a C call for >2s, the state is rebuilt. Overlay + GUI cleared. Journaled cfg/cheats/FOV restored.

When the main menu is closed, the overlay is click-through except Lua IDE, Lua GUI, or an active drag.

Y. Full API index

NamespaceWhat
vexor.mem.*memory r/w, vec3, bytes, hexdump, base, pid, alive
vexor.game.*datamodel addrs, place, light player list, PF/MM2
vexor.player.*your character: pos, vel, children, walkspeed
vexor.players.*full cache, teleport any, screen boxes, parts
vexor.inst.*name, class, parent, find, children, set_parent
vexor.part.*primitive pos/vel/size/rot/colour
vexor.humanoid.*walkspeed / health by address
vexor.camera.*pos, fov, matrix, subject, type
vexor.world.*to_screen, screen_size, on_screen, axes, distance3
vexor.draw.* / overlay.*overlay primitives, text_ex, measure, present, clear
vexor.esp.*box/corner/healthbar/tracer/skeleton/bone tables
vexor.gui.*windows, pickers, sliders, hotkeys, tabs
vexor.fonts.listfont names
vexor.color.*pack/lerp/hsv/lighten
vexor.math.*numeric
vexor.input.*keys/mouse
vexor.cheats.* / vexor.cfgoptional compiled settings
vexor.offsets.*field offsets
vexor.debug.*dump, hex, inspect, traceback, info, assert
sleep / present / stop / clock / now_ms / log / warn / notify / help
vexor.host / scripts_dir / print / dofile / loadfilehost is "vexor-menu-1"

Z. Copy-paste recipes

If Execute prints lua vm ok and you see overlay text plus a window, you are done with setup. Everything else is the same loop: clear, read cache or memory, draw or write, sleep, repeat. Terminate when finished.

1. Smoke test

print("lua vm ok", vexor.host, vexor.game.place_name())
vexor.debug.info()
print("pos", vexor.player.get_pos())
while true do
    vexor.draw.clear()
    local w,h = vexor.world.screen_size()
    vexor.draw.text_ex(20, 20, "Vexor Lua VM", 255,255,255,255, 18, "ui", 1)
    vexor.draw.crosshair(w*0.5, h*0.5, 8, 255,255,255,180, 1)
    vexor.gui.begin("Lua VM")
    vexor.gui.text("if you can drag this, input capture works")
    local r,g,b,a = vexor.gui.color("c", "Accent", 120, 180, 255, 255)
    vexor.gui.colored_text("picker live", r,g,b,a)
    vexor.gui.finish()
    vexor.sleep(1)
end

2. ESP from scratch (no vexor.cheats.set_esp)

while true do
    vexor.draw.clear()
    local me = vexor.players.local_player()
    local list = vexor.players.list()
    for i = 1, #list do
        local p = list[i]
        if p and not p["local"] then
            local x, y, w, h = vexor.players.screen_box(p.address)
            if x then
                local r, g, b = 255, 80, 80
                if p.team ~= 0 and me and p.team == me.team then
                    r, g, b = 80, 180, 255
                end
                vexor.esp.corner(x, y, w, h, r, g, b, 220, 1.5)
                vexor.esp.healthbar(x - 5, y, 3, h, p.health, p.max_health)
                local name = p.display_name
                if name == nil or name == "" then name = p.name end
                vexor.draw.text_ex(x, y - 16, name, 255,255,255,255, 13, "esp", 1)
            end
        end
    end
    vexor.sleep(1)
end

3. GUI teleport

while true do
    vexor.gui.begin("Movement", 40, 90)
    local x = vexor.gui.drag_float("tx", "X", 1, -10000, 10000, 0)
    local y = vexor.gui.drag_float("ty", "Y", 1, -10000, 10000, 50)
    local z = vexor.gui.drag_float("tz", "Z", 1, -10000, 10000, 0)
    if vexor.gui.button("go", "Teleport") then
        vexor.player.set_pos(x, y, z)
        print("teleported", x, y, z)
    end
    vexor.gui.finish()
    vexor.sleep(1)
end

4. Inspect whoever you are looking at

local me = vexor.players.local_player()
print("me", me)
if me then
    vexor.debug.inspect(me.address)
    if me.parts and me.parts.HumanoidRootPart then
        vexor.debug.hex(me.parts.HumanoidRootPart.primitive, 64)
    end
end

5. MM2 roles

if vexor.game.is_mm2() then
    for _, p in ipairs(vexor.players.list()) do
        print(p.name, vexor.game.mm2_role(p.address), p.mm2_role)
    end
end
getvexor docs · lua vm handbook · content written by ai download · discord