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:
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)andp:get_ability_name(slot)p:get_shot_damage([distance_m])andp: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, andraw_set,raw_write_status,raw_watch,raw_agetrace.line(from, to [, opts])trace.get(id),trace.stats()andtrace.release(id)(trace.releasereturnsfalse)hud.class_count(name)internal.ping(target)andinternal.ping_all()internal.set_rate(feed, ms)andinternal.get_rate(feed)p:get_ability_values(list)andp:set_property(name, value [, type])convar.*,chat.*andcmd.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
| Function | Returns |
|---|---|
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.
function on_tick()
local me = local_player()
if not me or not me:is_alive() or not internal.is_loaded() then return end
-- ...
endOr fall back instead of stopping:
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
endWhen a call returns nil
A feed with no answer returns nil, never 0 or a table of zeros. Check these in order:
internal.is_alive()is false.needs_internal = trueis missing fromscript_info. Look for the line intsuki.log.- 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. - A cap is full: 32 ability value pairs, 128 property pairs, 32 traces, shared by every loaded script.
- 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:
| Feed | Default | You can change it to | Name for set_rate |
|---|---|---|---|
| Stats | 100 ms | 20 to 1000 ms | "stats" |
| Ability values | 250 ms | 50 to 2000 ms | "abilities" |
| Entity properties | 250 ms | 50 to 2000 ms | "properties" |
| Traces | 50 ms | 25 to 1000 ms | "traces" |
| Hit events | On 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:
-- 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_tickThe 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:
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".
local i = tsuki.api_info("p:get_stats")
-- i.host = "internal", i.needs_internal = truePages
| Page | Covers |
|---|---|
| Stats | p: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 Values | p:get_ability_value(slot, name): the resolved number the tooltip shows, for any player |
| Hit Events | p:get_hit_events() and p:get_hit_stats(): per hit damage events and a running tally |
| Entity Properties | p:get_property(name) and row:get(name): read a game field by name |
| Schema Fields | obj.m_FieldName, raw_set and raw_watch: read, write and watch any game field |
| Traces | trace.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 |
| Aim | aim.silent, aim.shoot, aim.can_reach, aim.get_solution, aim.diag |
| Pings | internal.ping and internal.ping_all: a real map ping on an enemy |
| Chat and Map | chat.send, chat.ping, chat.map_line: chat, ping a spot, draw on the minimap |
| Commands | cmd.clear, cmd.hold, cmd.press, cmd.cast: change the buttons in your own game command, including taking one away |
| Glow | glow.set, glow.clear, glow.clear_all: outline or fill any player or entity |
| Console Settings | convar.get, convar.set_int, convar.set_float, convar.set_bool: read and change the game's console settings |
| HUD | hud.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.