Skip to content

Internal API Reference ​

Most of the Lua API is served by TSUKI itself and works in every script. The functions in this section are served by the internal, an optional component that has to be running. When it is not running they return nil and your script keeps going.

To use them, add needs_internal = true to your script_info:

lua
script_info = {
    name           = "my_script",
    needs_internal = true,
}

Adding the flag does not change how the rest of the API works.

What the flag gates ​

Without needs_internal = true these return nil and write one line to C:\TSUKI\Deadlock\logs\tsuki.log naming the script and the function (once per function, not once per call):

  • p:get_stats()
  • p:get_ability_value(slot, name)
  • p:get_ability_value_by_name(ability, name) and p:get_ability_name(slot)
  • p:get_shot_damage([distance_m]) and p:get_ability_damage(slot [, property [, kind]])
  • p:get_hit_events([since_seq])
  • p:get_hit_stats()
  • p:get_property(name)
  • entities.row:get(name)
  • obj.m_FieldName, and raw_set, raw_write_status, raw_watch, raw_age
  • trace.line(from, to [, opts])
  • trace.get(id), trace.stats() and trace.release(id) (trace.release returns false)
  • hud.class_count(name)
  • internal.ping(target) and internal.ping_all()
  • internal.set_rate(feed, ms) and internal.get_rate(feed)
  • p:get_ability_values(list) and p:set_property(name, value [, type])
  • convar.*, chat.* and cmd.mask()

glow.*, cmd.clear, cmd.hold, cmd.press, cmd.cast and cmd.use_item return false instead, and cmd.release returns 0. cmd.reset and cmd.on_change do nothing. Each writes the same log line.

The script still loads, shows in the menu and ticks. Nothing errors. The log line does not reach the in-app console, so a missing flag looks like a function that returns nil forever.

aim.* needs the internal running, except aim.can_reach, but does not check the flag. Declare the flag anyway if you use these functions.

Is the internal running ​

FunctionReturns
internal.is_loaded()true when the internal is present
internal.is_alive()true when it is present and responding
internal.version()Build number, 0 when nothing is loaded

Any script can call these. Check is_alive before you trust the numbers, because is_loaded stays true when the internal is present but stuck.

lua
function on_tick()
    local me = local_player()
    if not me or not me:is_alive() or not internal.is_loaded() then return end
    -- ...
end

Or fall back instead of stopping:

lua
local dmg = fallback
if internal.is_loaded() then
    local st = me:get_stats()
    if st and st.bullet_damage and st.bullet_damage > 0 then dmg = st.bullet_damage end
end

When a call returns nil ​

A feed with no answer returns nil, never 0 or a table of zeros. Check these in order:

  1. internal.is_alive() is false.
  2. needs_internal = true is missing from script_info. Look for the line in tsuki.log.
  3. Nothing asked for it recently. The stat feed only runs if a script has called p:get_stats() in the last 2 seconds. The ability value, property and trace feeds only work on names you have asked for.
  4. A cap is full: 32 ability value pairs, 128 property pairs, 32 traces, shared by every loaded script.
  5. It is the first call for a new request. That call registers it and returns nil. The value arrives on a later tick.

aim.silent and aim.shoot return false instead of nil when the internal is absent.

Refresh rates ​

Each feed refreshes at a fixed rate. These are the defaults:

FeedDefaultYou can change it toName for set_rate
Stats100 ms20 to 1000 ms"stats"
Ability values250 ms50 to 2000 ms"abilities"
Entity properties250 ms50 to 2000 ms"properties"
Traces50 ms25 to 1000 ms"traces"
Hit eventsOn each hit--

Asking for a faster value ​

If 250 ms is too slow for an ability value, there are two ways to change it, and they do different things.

Speed up one value. Pass a rate_ms as the last argument. Only that value gets faster:

lua
-- this cooldown refreshes every 25 ms; the other values stay at 250
local cd = me:get_ability_value(1, "AbilityCooldown", 25)

-- the same argument works on properties
local hp = me:get_property("m_iHealth", 50)
local t  = row:get("m_flCreateTime", 50)

-- and on traces, as an option
id = trace.line(from, to, { id = id, rate_ms = 25 })   -- id declared once, outside on_tick

The range is 25 to 2000 ms, or 25 to 1000 ms on traces. Pass it on every call so the feed knows you still want it. If you stop passing it, that value goes back to the normal rate after 2 seconds. If two scripts ask for different rates on the same value, the faster one wins, so neither script can slow the other down.

Speed up a whole feed. internal.set_rate(feed, ms) changes the rate for everything in that feed. It returns the rate now in use, which may have been clamped:

lua
internal.set_rate("abilities", 50)   --> 50
internal.get_rate("abilities")       --> 50
internal.set_rate("abilities", 10)   --> 50   (50 ms is this feed's floor)
internal.set_rate("abilities", 0)    --> 250  (back to the user's setting)

Both return nil for an unknown feed name, and for a moment after the internal loads, until the rate is known.

This rate is global. Every script shares it and the last call wins. It is cleared when the script that set it is turned off, errors or reloads. It is the same number the user's own settings control. Use it when you want the whole feed faster, such as for a debug overlay or a tool. Use rate_ms when you only want your own value faster.

internal.feeds() shows every feed in one table: the rate in use, what is registered, the limit, and how often the limit held a refresh back. It is the quickest way to check whether you are asking for too much.

When it cannot keep up ​

Only a limited number of ability values can refresh fast at the same time, and every script shares that limit. Past it, values refresh slower than they asked for and internal.ability_values_diag().rate_capped starts counting up. If it keeps climbing, ask for fewer fast values or use a longer rate_ms on them.

Checking a name from a script ​

tsuki.api() returns every registered name with its host (external or internal) and needs_internal flag. tsuki.api_info(name) returns one entry or nil. The name must match exactly, prefix included: "p:get_stats", not "get_stats".

lua
local i = tsuki.api_info("p:get_stats")
-- i.host = "internal", i.needs_internal = true

Pages ​

PageCovers
Statsp:get_stats(): resolved resists, shields, barrier, and your own weapon numbers. p:get_shot_damage() and p:get_ability_damage(): your damage on a player
Ability Valuesp:get_ability_value(slot, name): the resolved number the tooltip shows, for any player
Hit Eventsp:get_hit_events() and p:get_hit_stats(): per hit damage events and a running tally
Entity Propertiesp:get_property(name) and row:get(name): read a game field by name
Schema Fieldsobj.m_FieldName, raw_set and raw_watch: read, write and watch any game field
Tracestrace.line() and trace.get(): what a line hits. trace.bvh() checks a line against the map right away, without the internal, and trace.source() says which check the aimbot uses
Aimaim.silent, aim.shoot, aim.can_reach, aim.get_solution, aim.diag
Pingsinternal.ping and internal.ping_all: a real map ping on an enemy
Chat and Mapchat.send, chat.ping, chat.map_line: chat, ping a spot, draw on the minimap
Commandscmd.clear, cmd.hold, cmd.press, cmd.cast: change the buttons in your own game command, including taking one away
Glowglow.set, glow.clear, glow.clear_all: outline or fill any player or entity
Console Settingsconvar.get, convar.set_int, convar.set_float, convar.set_bool: read and change the game's console settings
HUDhud.class_count(name): how many interface panels carry a class, for reading the game's own cast indicator

Everything else is in the API Reference.