# Pulse — Complete Framework Reference Pulse is a reactive component framework for Roblox Lua. Source files (.rblua) are compiled into a single flat Lua chunk. No require(), no runtime module system. DISCLAIMER: Pulse scripts should only be used on games you own or have explicit permission to test on, and for learning purposes only. --- ## Component syntax A .rblua file wraps everything in `component { }`. The filename = the component name. ```lua component { -- Signal declarations (reactive state) signal enabled = false signal radius = 300 signal mode = "Normal" -- Remote/invoke wrappers (game-specific paths) remote Attack = "Remotes/Combat/Attack" -- FireServer wrapper invoke GetData = "Remotes/Data/Get" -- InvokeServer wrapper -- Cross-component store keys shared { GlobalTarget = nil } -- Init block: runs once at load. Locals here are upvalues for all handlers below. init { local RS = game:GetService("RunService") local obj = nil local function helper() -- private to this component end -- Bind connections so they auto-disconnect on cleanup MyComp:bind("conn", RS.Heartbeat:Connect(function() end)) -- BindToRenderStep needs a wrapper RS:BindToRenderStep("__Key__", Enum.RenderPriority.Last.Value, function() end) MyComp:bind("_rs", { Disconnect = function() RS:UnbindFromRenderStep("__Key__") end }) -- Game load guard for workspace/camera access task.spawn(function() if not game:IsLoaded() then game.Loaded:Wait() end -- safe to use workspace here end) -- Cleanup binds MUST go outside the spawn } -- Event handlers on Heartbeat { } -- every frame on Heartbeat when enabled { } -- only while enabled == true on Heartbeat when enabled every 0.5 { } -- throttled to 0.5s on InputBegan(input, gpe) { } -- keyboard/mouse on CharacterAdded { } -- respawn on enabled { } -- signal change: v = new value on radius { } -- signal change: v = new value -- One-shot delay after 2.5 { } -- Public methods func Toggle() { enabled(not enabled()) } -- UI widget declarations ui { toggle "Enable" -> enabled default=true tip="tooltip" slider "Radius" -> radius [50, 800] default=300 dropdown "Mode" -> mode ["A","B","C"] default="A" button "Reset" -> Reset() keybind "Key" key="N" -> Toggle() separator label "Section" } } ``` ## Signals - Declare: `signal name = defaultValue` (boolean, number, or string only) - Read inside on-handlers: use name directly (`if radius > 500`) - Read in func blocks and from outside: call it (`radius()`, `Aimbot.radius()`) - Write: call with value (`radius(800)`, `enabled(true)`) - React: `on signalName { -- v = new value }` - From another component: `Aimbot.enabled(true)`, `Aimbot.radius()` - Watch from another component: `MyComp:watch(Aimbot.enabled, function(v) end)` — auto-cleans up Limitations: table defaults not supported; writes are synchronous; no computed/derived signals built in. ## Character locals (available inside all on-handlers) - `humanoid` — LocalPlayer.Character.Humanoid (nil if absent) - `hrp` — HumanoidRootPart (nil if absent) - `character` — Character model (nil if absent) - `alive` — true if Health > 0 Always use `guard h = humanoid` before accessing. Never cache these across frames. ## Guard shorthand ```lua guard h = humanoid -- local h = humanoid; if not h then return end guard hrp -- if not hrp then return end ``` ## Connection tracking ```lua MyComp:bind("name", connection) -- tracked, disconnects on unmount MyComp:unbind("name") -- disconnect one MyComp:unbindAll() -- disconnect all MyComp:watch(signal, fn) -- subscribe, auto-unsubscribes on unmount ``` ## Component access ```lua Aimbot.enabled(true) -- write another component's signal local r = Aimbot.radius() -- read another component's signal Aimbot:Toggle() -- call another component's func Components.Aimbot.enabled(true) -- via registry ``` --- ## Helpers ### Pulse.Log ```lua Pulse.Log.trace("Tag", "msg", {data}) Pulse.Log.debug("Tag", "msg", {data}) Pulse.Log.info ("Tag", "msg", {data}) Pulse.Log.warn ("Tag", "msg", {data}) Pulse.Log.error("Tag", "msg", {data}) Pulse.Log.throttle("Tag", intervalSeconds, "level", "msg", {data}) Pulse.Log.assert(condition, "Tag", "msg") Pulse.Log.save("filename.log") Pulse.Log.clear() Pulse.Log.configure({ level="debug", tags={"Aimbot"} }) ``` Active only in --dev builds. Zero overhead in production. ### Pulse.Monitor ```lua Pulse.Monitor.set("key", value) -- set live dashboard value Pulse.Monitor.getAll() -- returns all key-value pairs ``` ### Pulse.Loop ```lua local loop = Pulse.Loop.new(interval, function() -- return false to self-stop end, "tagName") loop:start() loop:stop() loop:running() -- bool loop:setInterval(n) -- change while running ``` Errors are caught, throttle-logged (5s), loop continues. ### Pulse.Draw ```lua -- Highlights (note: for other players/replicated models use CoreGui + Adornee) Pulse.Draw.highlight(target, "Key", { fill=Color3, outline=Color3, fillAlpha=0.5 }) Pulse.Draw.removeHighlight(target, "Key") -- Screen circles local ring = Pulse.Draw.circle({ radius=300, color=Color3, thickness=2, alpha=0.8, filled=false, sides=64, visible=true }) ring.Position = Vector2.new(x, y) -- update each frame ring.Radius = r ring.Visible = v Pulse.Draw.remove(ring) -- Selection boxes Pulse.Draw.selectionBox(part, "Key", { color=Color3, thickness=0.05 }) Pulse.Draw.removeHighlight(part, "Key") ``` IMPORTANT: Highlights parented to replicated objects (other players' chars, server models) are destroyed by Roblox replication. Use this pattern instead: ```lua local h = Instance.new("Highlight") h.Adornee = target h.Parent = game:GetService("CoreGui") -- or PlayerGui as fallback ``` ### Pulse.Aim ```lua Pulse.Aim.inFOV(worldPos, pixelRadius) -- bool: inside screen circle? Pulse.Aim.screenDist(worldPos) -- pixel distance from screen centre Pulse.Aim.lookAt(worldPos) -- snap camera (needs VirtualInputManager) local nearest = Pulse.Aim.findNearest(candidates, { fovRadius = 300, -- optional pixel radius maxDist = 500, -- optional studs cap getRoot = function(e) return e:FindFirstChild("Head") end, -- optional filter = function(e) return not isDead(e) end, -- optional }) -- Lock-on locker local _lock = Pulse.Aim.locker({ getPos = function(target) return target.Head.Position end, validate = function(target) return target and target.Parent and not isDead(target) end, }) _lock:lock(target) _lock:release() _lock:getTarget() -- current target or nil _lock:aim() -- call every RenderStepped frame, returns false if target lost ``` IMPORTANT: For large NPCs, use Nape hitbox as getRoot, not HumanoidRootPart (waist-level, often off-screen). ### Pulse.Hitbox ```lua local hm = Pulse.Hitbox.new("uniqueKey") hm:apply(part, size) -- save original + resize to Vector3.new(size,size,size) hm:restore(part) -- restore one part hm:restoreAll() -- restore all managed parts ``` ### Pulse.Team (resolver pattern) ```lua local resolver = Pulse.Team.resolver({ isSelf = function(e) return e == LocalPlayer end, isValid = function(e) return e.Character ~= nil end, isHostile = function(e) return teamA ~= teamB end, exclude = { allySpecialCaseFn }, }) resolver:isEnemy(e) -- bool resolver:filter(list) -- enemies only resolver:partition(list) -- { enemies={}, friendlies={} } ``` ### Pulse.Store ```lua Pulse.Store.define("Key", defaultValue) Pulse.Store.get("Key") Pulse.Store.set("Key", value) Pulse.Store.watch("Key", function(newValue) end) Pulse.Store.signal("Key") -- raw Signal object ``` ### Pulse.Cooldown ```lua local cd = Pulse.Cooldown.new(1.0) cd:ready() cd:use() cd:reset() cd:set(dur) local pcd = Pulse.Cooldown.perEntity(0.5) pcd:ready(key) pcd:use(key) pcd:reset(key) pcd:purge(function(k) return isDead(k) end) -- always call this in a periodic loop ``` ### Pulse.Track ```lua local tracker = Pulse.Track.new() tracker:apply(entity, function(e) -- cleanup called on remove end) tracker:remove(entity) tracker:cleanup(function(e) return isDead(e) end) tracker:clear() tracker:has(entity) -- bool tracker:count() -- number tracker:each(fn) ``` ### Pulse.Cache ```lua local get = Pulse.Cache.wrap(2, function() return expensiveCall() end) local result = get() -- cached for 2 seconds local c = Pulse.Cache.new(5) c:set(key, value) c:get(key) -- nil after 5s c:has(key) c:del(key) c:clear() ``` ### Pulse.World ```lua Pulse.World.find("Folder", "Sub") -- nil if missing Pulse.World.children("Folder", "Sub") -- {} if missing Pulse.World.service("TweenService") Pulse.World.await(parent, "Child", timeout) ``` ### Pulse.Restore ```lua local r = Pulse.Restore.new() r:save(instance, "Property") r:one(instance, "Property") r:all() r:has(instance, "Property") ``` --- ## Common patterns ### Start/stop with a signal ```lua init { local _loop = Pulse.Loop.new(1.0, function() end) local function run(v) if v then _loop:start() else _loop:stop() end end } on enabled { run(v) } ``` ### CoreGui highlight (replication-safe) ```lua local h = Instance.new("Highlight") h.FillColor = Color3.fromRGB(255, 50, 50) h.Adornee = targetModel pcall(function() h.Parent = game:GetService("CoreGui") end) if not h.Parent then h.Parent = game:GetService("Players").LocalPlayer.PlayerGui end ``` ### Game load guard + cleanup outside spawn ```lua init { task.spawn(function() if not game:IsLoaded() then game.Loaded:Wait() end RS:BindToRenderStep("__Key__", Enum.RenderPriority.Last.Value, fn) end) MyComp:bind("_rs", { Disconnect = function() RS:UnbindFromRenderStep("__Key__") end }) } ``` --- ## Common mistakes to avoid 1. `func.X = function()` inside init → use `local function X()` 2. Caching `humanoid` at init time → always read inside handlers via `guard h = humanoid` 3. No `guard` before `humanoid.Health` → always guard 4. `GetChildren()` in Heartbeat → use Pulse.Loop at 1s interval 5. No game load guard for workspace access → use task.spawn + game.Loaded:Wait() 6. `task.wait()` inside signal change handlers → use task.spawn if async work needed 7. `wait()` instead of `task.wait()` → always use task library 8. Connections without component:bind → always bind every connection 9. Parenting highlights to replicated objects → use CoreGui + Adornee 10. Signal with table default → use local variable in init instead --- ## CLI commands ```bash py rb/rb.py build # compile + check + obfuscate py rb/rb.py build --dev # include dev overlay py rb/rb.py build --no-obfuscate # plain output only py rb/rb.py copy # compile + copy to clipboard py rb/rb.py watch # auto-rebuild on file save py rb/rb.py lint # find dead signals, ghost refs py rb/rb.py check # syntax-check only ``` Output files: build/script.lua (plain), build/script.obf.lua (inject this), build/script.dev.obf.lua (dev build).