Lua API Reference
Complete reference for the gamesense.cloud Lua scripting API. All modules follow the Starline convention — global tables with PascalCase function names. Entity functions transparently resolve controllers to pawns for CS2 compatibility.
globals
Timing and frame information from the Source 2 engine global variables.
globals.RealTime() → numberglobals.CurTime() → numberglobals.FrameTime() → numberglobals.FrameCount() → integerglobals.TickCount() → integerglobals.TickInterval() → numberglobals.MaxPlayers() → integerglobals.MapName() → string"de_dust2"). Empty string when not connected.globals.IsConnected() → booleanengine
Engine queries, view control, and console commands.
engine.GetLocalPlayer() → userdata | 0engine.GetMaxPlayers() → integerengine.GetMapName() → stringengine.GetViewAngles() → pitch, yaw, rollengine.SetViewAngles(pitch, yaw, roll)engine.IsConnected() → booleanengine.IsInGame() → booleanengine.ExecuteCommand(cmd: string)"say hello").engine.GetScreenSize() → width, heightengine.WorldToScreen(x, y, z) → {x, y} | nilengine.GetCurTime() → numberengine.GetRealTime() → numberengine.GetFrameTime() → numberengine.GetTickRate() → numberengine.GetTickCount() → integerengine.GetRoundPhase() → string"live", "freezetime", or "over". Updated automatically from game events (round_start, round_freeze_end, round_end).entity
Entity access and queries. Indices 1–64 are player controllers in CS2 — this module transparently resolves them to pawns so scripts get position, health, and team data from the correct entity.
entity.GetLocalPlayer() → userdata | nilentity.GetByIndex(index: integer) → userdata | nilentity.GetPlayers() → tableentity.GetHealth(ent) → integerentity.GetArmor(ent) → integerentity.GetTeam(ent) → integerentity.GetName(ent) → stringentity.GetPosition(ent) → x, y, zm_vOldOrigin from the pawn).entity.GetEyePosition(ent) → x, y, zentity.GetViewAngles(ent) → pitch, yaw, rollm_angEyeAngles).entity.IsAlive(ent) → booleanm_lifeState == 0.entity.IsDormant(ent) → booleanentity.GetBoundingBox(ent) → x, y, w, h, alpha | nilentity.GetHitboxPosition(ent, hitbox: integer) → x, y, zentity.GetWeapon(ent) → userdata | nilm_pClippingWeapon).entity.GetWeaponName(ent) → stringweapon_ prefix stripped.entity.GetController(pawn: userdata) → userdata | nilentity.GetPawn(controller) → userdata | nilentity.IsEnemy(ent) → booleanentity.GetFlags(ent) → integerm_fFlags). Bit 1 = crouching.entity.IsScoped(ent) → booleanentity.HasHelmet(ent) → booleanentity.HasDefuser(ent) → booleanentity.GetFlashDuration(ent) → numberentity.GetMoney(ent) → integerm_pInGameMoneyServices.entity.GetColor(ent) → integerentity.GetVelocity(ent) → x, y, zm_vecVelocity).entity.GetSteamID(ent) → integerentity.GetIndex(ent: userdata) → integerentity.GetPropInt(ent, class: string, field: string) → integerentity.GetPropInt(ent, "C_BaseEntity", "m_iHealth")entity.GetPropFloat(ent, class: string, field: string) → numberentity.GetPropBool(ent, class: string, field: string) → booleanentity.GetPropVec3(ent, class: string, field: string) → x, y, zentity.GetPropString(ent, class: string, field: string) → stringentity.GetEntityFromHandle(handle: integer) → entity | nilExample — iterate enemies
local players = entity.GetPlayers()
for _, ply in ipairs(players) do
if entity.IsEnemy(ply) and entity.IsAlive(ply) then
local name = entity.GetName(ply)
local hp = entity.GetHealth(ply)
local x, y, z = entity.GetPosition(ply)
print(name .. " has " .. hp .. "hp at " .. x .. ", " .. y)
end
endExample — read custom netvar
local ent = entity.GetByIndex(1)
if ent then
local kills = entity.GetPropInt(ent, "CCSPlayerController", "m_iKills")
local ping = entity.GetPropInt(ent, "CCSPlayerController", "m_iPing")
print("Kills: " .. kills .. " Ping: " .. ping)
endrenderer
2D rendering API. All drawing functions must be called inside a paint event handler. Colors can be Color() objects, {r, g, b, a} tables, or packed integers.
renderer.Line(x1, y1, x2, y2 [, color, thickness])renderer.Rect(x, y, w, h [, color, rounding])renderer.RectFilled(x, y, w, h [, color, rounding])renderer.GradientRect(x, y, w, h [, colorA, colorB])renderer.Circle(x, y, radius [, color, segments])renderer.CircleFilled(x, y, radius [, color, segments])renderer.Triangle(x1, y1, x2, y2, x3, y3 [, color])renderer.TriangleFilled(x1, y1, x2, y2, x3, y3 [, color])renderer.Polyline(points [, color, thickness])points is an array of {x, y} tables (minimum 3).renderer.Polygon(points [, color, thickness])points is an array of {x, y} tables (minimum 3).renderer.PolygonFilled(points [, color])renderer.RoundedRect(x, y, w, h, rounding [, color])renderer.RoundedRectFilled(x, y, w, h, rounding [, color])renderer.Arc(cx, cy, radius, startAngle, endAngle [, color, segments, thickness])renderer.ArcFilled(cx, cy, radius, startAngle, endAngle [, color, segments])renderer.Text(x, y, text [, color, size, font])"interface", "strong", "mono", or "display".renderer.TextEx(x, y, text [, color, size, font, alignX, alignY])alignX is "left", "center", or "right". alignY is "top", "center", or "bottom".renderer.MeasureText(text [, size, font]) → width, heightrenderer.ScreenSize() → width, heightrenderer.WorldToScreen(x, y, z) → sx, sy | nilExample — draw crosshair + info
events.On("paint", function()
local w, h = renderer.ScreenSize()
local cx, cy = w / 2, h / 2
-- crosshair
renderer.Line(cx - 8, cy, cx + 8, cy, Color(0, 255, 0, 200))
renderer.Line(cx, cy - 8, cx, cy + 8, Color(0, 255, 0, 200))
-- velocity display
local me = entity.GetLocalPlayer()
if me then
local vx, vy, vz = entity.GetVelocity(me)
local speed = math.floor(math.sqrt(vx*vx + vy*vy))
renderer.Text(cx, cy + 20, speed .. " u/s",
Color(255, 255, 255, 180), 14, "mono")
end
end)input
Keyboard and mouse state. Key codes are exposed as constants on the input table (e.g. input.KEY_A, input.MOUSE_LEFT, input.F1–input.F12).
input.IsKeyDown(key: integer) → booleaninput.IsKeyPressed(key: integer) → booleaninput.IsKeyReleased(key: integer) → booleaninput.GetMousePos() → x, yinput.GetMouseWheel() → numberinput.GetKeyName(key: integer) → stringinput.IsMouseDown(button: integer) → booleaninput.IsMousePressed(button: integer) → booleaninput.IsMouseReleased(button: integer) → booleaninput.GetMouseDelta() → dx, dyKey Constants
MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE, MOUSE_4, MOUSE_5, BACKSPACE, TAB, ENTER, SHIFT, CTRL, ALT, ESCAPE, SPACE, LEFT/RIGHT/UP/DOWN, KEY_0–KEY_9, KEY_A–KEY_Z, F1–F12, INSERT, DELETE, HOME, END, PAGE_UP, PAGE_DOWN, CAPS_LOCK
ui
Tab / Group / Widget hierarchy for building script configuration UIs. Values persist automatically across script reloads.
ui.Tab(name: string) → tabui.GetValue(id: string) → valueui.SetValue(id: string, value)tab:Group(name) → group
Create a named group within a tab.
Group Widgets
group:Checkbox(label, default: boolean) → controlgroup:SliderInt(label, min, max [, default]) → controlgroup:SliderFloat(label, min, max [, default]) → controlgroup:Combo(label, options: table [, default_index]) → controlgroup:Multiselect(label, options: table [, defaults: table]) → controlgroup:Button(label [, callback]) → controlgroup:ColorPicker(label [, default: {r,g,b,a}]) → controlgroup:Textbox(label [, default: string]) → controlgroup:Keybind(label [, default_key]) → controlgroup:Label(text) → controlgroup:Separator([text]) → controlControl Handle Methods
control:Get() → valuecontrol:Set(value)control:OnChange(callback) → selfProperties: .id, .kind, .label
Example — full UI setup
local tab = ui.Tab("Aim Helper")
local g = tab:Group("Settings")
local enabled = g:Checkbox("Enabled", true)
local fov = g:SliderFloat("FOV", 1.0, 30.0, 5.0)
local style = g:Combo("Style", {"Circle", "Cross", "Dot"}, 1)
local hotkey = g:Keybind("Toggle Key", input.KEY_X)
local color = g:ColorPicker("Color", {1, 0, 0, 1})
g:Separator("Info")
local status = g:Label("Status: idle")
enabled:OnChange(function(val)
status:Set(val and "Status: active" or "Status: idle")
end)
g:Button("Reset Defaults", function()
fov:Set(5.0)
style:Set(1)
color:Set({1, 0, 0, 1})
end)events
Event subscription system with typed event names.
events.On(name: string, callback: function) → handle: integerevents.Off(handle: integer) → booleanevents.List() → tableConstants
events.BUTTON_FORCE_OFF (0), events.BUTTON_TOGGLE (1), events.BUTTON_FORCE_ON (2)
Event Reference
Platform and engine events pass individual arguments to callbacks. CS2 game events pass a single table with the listed fields — e.g. function(e) print(e.userid) end.
framedt: numberpaint(none — draw here)keykey: number, down: booleanresizewidth: number, height: numberfocusfocused: booleanunload(none)createmovecmd: CUserCmdmovementcmd: CUserCmdframe_stagestage: numbervote_setup(none)edgebug(none)jumpbug(none)pixelsurf(none)round_starte.timelimit: numberround_ende.winner, e.reason, e.messageround_freeze_end(none)round_mvpe.userid, e.reasonbegin_new_match(none)announce_phase_end(none)player_deathe.userid, e.attacker, e.headshot, e.distance, e.weapon, e.assistedflash, e.noscope, e.thrusmoke, e.penetrated, e.dominated, e.revenge, e.assisterplayer_hurte.userid, e.attacker, e.health, e.armor, e.dmg_health, e.dmg_armor, e.hitgroup, e.weaponplayer_spawne.userid, e.teamplayer_disconnecte.userid, e.reasonitem_purchasee.userid, e.team, e.weaponweapon_firee.userid, e.weaponbullet_impacte.userid, e.x, e.y, e.zbomb_plantede.useridbomb_defusede.useridbomb_exploded(none)flashbang_detonatee.userid, e.entityid, e.x, e.y, e.zsmokegrenade_detonatee.userid, e.entityid, e.x, e.y, e.zhegrenade_detonatee.userid, e.x, e.y, e.zinferno_startburne.entityid, e.x, e.y, e.zinferno_expiree.entityid, e.x, e.y, e.zdecoy_startede.userid, e.entityid, e.x, e.y, e.zExample — track kills
local myKills = 0
-- CS2 game events pass a single table with named fields
events.On("player_death", function(e)
local me = entity.GetLocalPlayer()
if me and e.attacker == entity.GetIndex(me) then
myKills = myKills + 1
cheat.Notify("Kill #" .. myKills .. " with " .. e.weapon .. "!")
system.PlaySound("scripts/ding.wav")
end
end)
events.On("round_start", function(e)
myKills = 0
end)
-- Platform events pass individual arguments
events.On("key", function(key, down)
if key == input.KEY_H and down then
cheat.Notify("Kills this round: " .. myKills)
end
end)hooks
Named hook management — bridges hook names to the event system. Supports both PascalCase and snake_case hook names.
hooks.Add(hookName, uniqueId, callback)hooks.Remove(hookName, uniqueId)Supported Hooks
CreateMoveMovementFrameStageNotifyPaint / DrawFrameKeyResizeFocusUnloadRoundStartRoundEndFreezeTimeEndRoundMVPBeginNewMatchAnnouncePhaseEndPlayerDeathPlayerHurtPlayerSpawnPlayerDisconnectItemPurchaseWeaponFireBulletImpactBombPlantedBombDefusedBombExplodedFlashbangDetonateSmokegrenadeDetonateHEGrenadeDetonateInfernoStartBurnInfernoExpireDecoyStartedVoteSetupEdgeBugJumpBugPixelSurfExample — hooks.Add vs events.On
-- hooks.Add uses named IDs (can replace/remove by name)
hooks.Add("Paint", "my_watermark", function()
renderer.Text(10, 10, "gamesense.cloud", Color(100, 200, 255), 16)
end)
-- remove later by name
hooks.Remove("Paint", "my_watermark")
-- events.On uses numeric handles
local h = events.On("paint", function()
renderer.Text(10, 10, "hello", Color(255,255,255))
end)
events.Off(h) -- remove by handleesp
ESP override and custom element system. Store per-entity overrides, custom text, and custom bars that a renderer can consume.
esp.Override(entity_index, property, value)esp.CustomText(entity_index, position, text [, color])"top" or "bottom". Cleared every frame — re-add in your paint handler.esp.CustomBar(entity_index, position, value [, color])esp.GetOverride(entity_index, property) → value | nilesp.Clear()esp.ClearEntity(entity_index)http
HTTP client via WinHTTP. Supports HTTPS. Requests are synchronous — use timer.After(0, fn) to avoid blocking paint. 10-second timeout.
http.Get(url [, callback]) → body | nil, errorcallback(body, error). Without: returns body directly, or nil + error string.http.Post(url, body [, callback [, content_type]]) → body | nil, errorapplication/json.http.Request({url, method, body, content_type}) → {status, body, ok, error}Example — fetch & post JSON
-- simple GET
local body, err = http.Get("https://api.example.com/data")
if body then
local data = json.Decode(body)
print("Got " .. #data .. " items")
end
-- POST JSON (non-blocking via timer)
timer.After(0, function()
local payload = json.Encode({ name = cheat.GetUsername() })
local res = http.Request({
url = "https://api.example.com/submit",
method = "POST",
body = payload,
})
if res.ok then print("Submitted!") end
end)cheat
Cheat identity, control, and utility functions.
cheat.GetCheatName() → string"gamesense.cloud".cheat.GetVersion() → stringcheat.GetUsername() → stringcheat.IsLoaded() → booleancheat.Unload()cheat.Reload()cheat.Log(text: string)cheat.SetClantag(tag: string)cheat.Notify(text: string)cheat.GetTimestamp() → string"YYYY-MM-DD HH:MM:SS".cheat.FindExport(module: string, export: string) → userdata | nilcheat.IsRadarActive() → booleancheat.GetRadarURL() → string | nilcheat.GetRadarStats() → table{ active, pushCount, failCount, avgLatency, playerCount, uptime } with live radar statistics. Works whether radar is active or not.system
OS-level utilities — clipboard, timing, audio.
system.GetClipboard() → stringsystem.SetClipboard(text: string)system.GetTimestamp() → integersystem.GetTickCount() → integersystem.PlaySound(path: string)math
Extended math utilities for angle and vector operations. Augments the standard Lua math table — all functions are accessed via math.AngleNormalize() etc.
math.AngleNormalize(angle: number) → numbermath.AngleDifference(a: number, b: number) → numbermath.VectorAngles(x, y, z) → pitch, yawmath.AngleVectors(pitch, yaw) → fx, fy, fzmath.VectorLength(x, y, z) → numbermath.VectorDistance(x1, y1, z1, x2, y2, z2) → numbermath.Lerp(t, a, b) → numbera + t * (b - a).math.Clamp(value, min, max) → numberjson
JSON encoding and decoding. Both PascalCase and lowercase names are supported.
json.Decode(str: string) → table | niljson.Encode(value: any [, pretty: boolean]) → stringtrue as second arg for indented output.json.Valid(str: string) → booleanAliases: json.decode, json.encode, json.valid
store
Per-script persistent key-value storage. Data is saved as JSON and survives script reloads.
store.get(key: string) → value | nilstore.set(key: string, value: any)store.has(key: string) → booleanstore.remove(key: string)store.clear()store.keys() → tablestore.save()Example — persistent settings
-- load saved config or use defaults
local config = store.get("config") or {
enabled = true,
color = {255, 0, 0, 255},
key = input.KEY_H,
}
-- update and save on change
events.On("key", function(key, down)
if key == input.F2 and down then
config.enabled = not config.enabled
store.set("config", config)
cheat.Notify("Toggled: " .. tostring(config.enabled))
end
end)file
Filesystem access sandboxed to the scripts root directory. All paths are relative to the root; attempts to escape via .. are rejected.
file.Read(path: string) → string | nilfile.Write(path: string, content: string)file.Append(path: string, content: string)file.Exists(path: string) → booleanfile.IsDirectory(path: string) → booleanfile.Size(path: string) → integerfile.List(path: string) → tablefile.MakeDirectory(path: string) → booleanfile.Remove(path: string) → booleanfile.Root() → stringtimer
Deferred execution and periodic tasks.
timer.After(delay: number, callback: function) → handle: integerdelay seconds.timer.Every(interval: number, callback: function) → handle: integerinterval seconds. Return false from the callback to stop.timer.NextFrame(callback: function) → handle: integertimer.After(0, fn).timer.Cancel(handle: integer) → booleantimer.Count() → integerExample — periodic & one-shot
-- auto-save config every 30 seconds
timer.Every(30, function()
store.save()
return true -- keep running (return false to stop)
end)
-- delayed notification
timer.After(3, function()
cheat.Notify("Script loaded!")
end)
-- do something next frame (avoids blocking paint)
timer.NextFrame(function()
http.Get("https://example.com/check")
end)log
Logging API. print() is redirected to log.info, so standard Lua prints appear in the console.
log.debug(...)log.info(...)log.warn(...)log.error(...)log.write(level: string, ...)"debug", "info", "warn", or "error".ffi
LuaJIT-compatible FFI subset for calling Windows API functions directly from Lua. Works on x64 Windows — all calling conventions collapse to Microsoft x64 ABI.
ffi.cdef(declaration: string)ffi.new(ctype: string [, size]) → cdata"char[4096]", "int[?]" (VLA — pass size as second arg).ffi.cast(ctype: string, value) → integerffi.string(cdata [, len]) → stringffi.sizeof(ctype | cdata) → integerffi.copy(dst, src [, len])ffi.fill(dst, len [, byte])ffi.load(library: string) → tableffi.typeof(cdata) → stringffi.gc(cdata, finalizer) → cdatanil to remove the finalizer.ffi.abi(param: string) → boolean"win", "64bit", and "le".ffi.C
Auto-resolving table of system library exports. Access any function from kernel32, user32, advapi32, ntdll, ws2_32, shell32, gdi32, ole32, msvcrt, winhttp, or crypt32 directly:
local result = ffi.C.MessageBoxA(0, "Hello", "Title", 0)
CData Object
Indexable buffer with bounds checking. Supports [index] read/write (0-based), #cdata for total size in bytes, and tostring().
Example — Windows API via ffi
-- allocate a buffer and call GetModuleFileNameA
local buf = ffi.new("char[260]")
ffi.C.GetModuleFileNameA(0, buf, 260)
print("Exe: " .. ffi.string(buf))
-- load a DLL and call an export
local ntdll = ffi.load("ntdll")
local ticks = ntdll.NtGetTickCount()
print("Ticks: " .. ticks)
-- MessageBox popup
ffi.C.MessageBoxA(0, "Hello from Lua!", "gscloud", 0)cvar
Console variable access — read and write cvars directly.
cvar.GetInt(name: string) → integercvar.GetFloat(name: string) → numbercvar.GetString(name: string) → stringcvar.SetInt(name: string, value: integer)cvar.SetFloat(name: string, value: number)cvar.SetString(name: string, value: string)cvar.Find(name: string) → userdata | niltracestub
Ray tracing queries. Currently returns default results (no hit, fraction 1.0). The API signature is stable — implementations will be added when the engine trace interface is hooked.
trace.Line(from_x, from_y, from_z, to_x, to_y, to_z [, skip_ent, mask]) → TraceResulttrace.Hull(from_x, from_y, from_z, to_x, to_y, to_z [, mins, maxs, skip_ent, mask]) → TraceResulttrace.Bullet(from_ent, to_ent) → {damage, hit}TraceResult
{ fraction, hit, entity, hitpos = {x,y,z}, normal = {x,y,z} }
panoramastub
Panorama UI scripting bridge. Currently a no-op — requires hooking the Panorama JS engine.
panorama.Execute(code: string)panorama.Listen(event: string, callback: function) → handle: integerTypes
Global constructors for common value types. These are available without a module prefix.
Color(r, g, b [, a])
RGBA color (0–255). Default alpha is 255.
Methods: :r(), :g(), :b(), :a() — getters
Methods: :SetR(v), :SetG(v), :SetB(v), :SetA(v) — setters
Supports tostring(): "Color(255, 0, 0, 255)"
Vector(x, y, z)
3D vector with full arithmetic.
Methods: :x(), :y(), :z()
:Length(), :Length2D(), :Dot(other), :Cross(other), :Normalized()
Operators: +, -, * (scalar), ==
QAngle(pitch, yaw, roll)
Euler angle triplet.
Methods: :pitch(), :yaw(), :roll()
Supports tostring().
Pointer(address)
Raw memory pointer with SEH-safe read/write operations.
:GetAddress() — returns the raw address as an integer
:IsValid() — true if address ≠ 0
Read: :ReadByte(off), :ReadShort(off), :ReadInt(off), :ReadInt64(off), :ReadFloat(off), :ReadDouble(off), :ReadPointer(off)
Write: :WriteByte(off, val), :WriteInt(off, val), :WriteFloat(off, val)
:Add(offset) — returns a new Pointer at address + offset
Quick Start Examples
ESP Script
-- Create a UI tab with controls
local tab = ui.Tab("My Script")
local grp = tab:Group("Settings")
local enabled = grp:Checkbox("Enable ESP", true)
local color = grp:ColorPicker("Box Color", {1, 0, 0, 1})
-- Draw ESP boxes on paint
hooks.Add("Paint", "my_esp", function()
if not enabled:Get() then return end
local players = entity.GetPlayers()
for _, ply in ipairs(players) do
if entity.IsEnemy(ply) and entity.IsAlive(ply) then
local x, y, w, h = entity.GetBoundingBox(ply)
if x then
renderer.Rect(x, y, w, h, color:Get())
local name = entity.GetName(ply)
renderer.Text(x, y - 12, name, {255,255,255,255}, 11)
end
end
end
end)HUD Overlay with Stats
local tab = ui.Tab("HUD")
local g = tab:Group("Display")
local showSpeed = g:Checkbox("Show Speed", true)
local showClock = g:Checkbox("Show Clock", true)
local kills = 0
events.On("player_death", function(e)
local me = entity.GetLocalPlayer()
if me and e.attacker == entity.GetIndex(me) then
kills = kills + 1
end
end)
events.On("round_start", function(e)
kills = 0
end)
events.On("paint", function()
local w, h = renderer.ScreenSize()
local y = 60
-- kill counter
renderer.RectFilled(w - 140, y, 130, 28, Color(0, 0, 0, 150), 4)
renderer.Text(w - 130, y + 6, "Kills: " .. kills,
Color(255, 80, 80), 14, "strong")
y = y + 34
-- speedometer
if showSpeed:Get() then
local me = entity.GetLocalPlayer()
if me then
local vx, vy = entity.GetVelocity(me)
local speed = math.floor(math.sqrt(vx*vx + vy*vy))
renderer.RectFilled(w - 140, y, 130, 28, Color(0, 0, 0, 150), 4)
renderer.Text(w - 130, y + 6, speed .. " u/s",
Color(200, 220, 255), 14, "mono")
y = y + 34
end
end
-- clock
if showClock:Get() then
renderer.RectFilled(w - 140, y, 130, 28, Color(0, 0, 0, 150), 4)
renderer.Text(w - 130, y + 6, cheat.GetTimestamp(),
Color(180, 180, 180), 11, "mono")
end
end)