Memory
Direct memory reads, for offsets you have found yourself.
If you have no offsets of your own, use the player and world objects instead. They expose everything TSUKI itself uses.
Functions
| Function | Returns | Description |
|---|---|---|
memory.read(type, address) | varies | Read a single value at an address |
memory.read_chain(type, base, offsets) | varies | Follow a pointer chain and read the final value |
memory.batch_read(entries) | table | Read multiple values in one call |
memory.get_client_base() | integer | Base address of the game client module |
memory.get_entity_list() | integer | Pointer to the entity list |
Supported types
| Type string | Reads |
|---|---|
"float" | 4-byte float |
"double" | 8-byte double |
"int" | 4-byte signed int |
"uint" | 4-byte unsigned int |
"int64" | 8-byte signed int |
"uint64" | 8-byte unsigned int |
"short" | 2-byte signed int |
"ushort" | 2-byte unsigned int |
"byte" | 1-byte unsigned int |
"bool" | 1-byte boolean |
"ptr" | Pointer-sized integer |
"pointer" | Alias for "ptr" |
read
Single value at an address.
local base = memory.get_client_base()
local some_offset = 0x1A2B3C4
local x = memory.read("float", base + some_offset)read_chain
Follow a pointer chain. base is the starting address and is not dereferenced. Each offset except the last is added to the current address, and the pointer stored there becomes the new address. The first step is Read<ptr>(base + offsets[1]). The last offset is added and the value there is read as type, so with one offset this reads type at base + offsets[1].
-- Equivalent to: *(float*)(*(*(base + o1) + o2) + o3)
local hp = memory.read_chain("float", base, { 0x10, 0x28, 0x4C })batch_read
Read many values in one call. This is cheaper than calling read in a loop.
Each entry is a positional array { type_string, address }. The type string is [1] and the address is [2]. Named keys are not supported.
local results = memory.batch_read({
{ "float", 0x12340000 },
{ "int", 0x12340004 },
{ "ptr", 0x12340008 },
})
-- results[1], results[2], results[3]get_client_base / get_entity_list
local client = memory.get_client_base()
local list = memory.get_entity_list()Use these as starting points if you have offsets relative to the client module or relative to the entity list.
Caveats
- A bad address returns
0ornil.readreturnsnilfor address0or an unknown type string.read_chainreturnsnilwhenbaseor any pointer along the chain is0. batch_readreads at most 64 entries. Entries past index 64 are ignored with no error.- Within a
batch_readresult, entries with address0or an unrecognised type string returnnil(not0) at that index. - Offsets shift between game patches, so update your own after a patch.
- Call these from
on_tickor a callback. In top-level code that runs when the script loads,get_client_base()andget_entity_list()can return0, and readsnil. - Memory reads are slower than the player/world API. Cache results if you're reading the same address every tick.