Reading raw memory every tick withDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/CryZe/asr-assemblyscript/llms.txt
Use this file to discover all available pages before exploring further.
Process.read works, but it leaves you writing the same boilerplate over and over: allocate a buffer, call read, parse the bytes, compare against the previous value, and decide whether something changed. What you almost always care about is the transition — a level ID going from 1 to 2, a loading flag flipping from false to true, a player position crossing a threshold — not just the instantaneous current value. The Watcher classes in asr-assemblyscript/watcher wrap this pattern into a tidy, strongly-typed API: declare a watcher at module level, call update() on it each tick, and read .current, .old, and .changed to react to transitions without any buffer management.
How Watchers Work
Every Watcher class stores two values:current— the value read during the most recent call toupdate().old— the value thatcurrentheld before the most recentupdate().
watcher.update(processId) performs the following steps atomically within a single tick:
- Copy
currentintoold. - Resolve the module base address via
Process.getModuleAddress. - Read bytes from
moduleBase + addressinto an internal buffer. - Parse and store the result as
current. - Return
true(and set.changedtotrue) ifcurrent !== old.
changed getter is equivalent to current !== old and is provided as a convenience.
The changed Convenience Return Value
watcher.update(processId) returns a bool — true if the value changed on this tick. This is identical to reading .changed after the call and lets you write compact conditional expressions:
The return value of
update() and the .changed getter always agree — both
reflect whether current !== old after the most recent update. Use whichever
style fits your code better; there is no functional difference.Available Watcher Classes
All watcher classes are exported fromasr-assemblyscript/watcher. Import the ones you need:
Numeric and Boolean Watchers
These all share the same constructor signature:| Class | Watched type | Byte size |
|---|---|---|
BoolWatcher | bool | 1 |
I8Watcher | i8 | 1 |
I16Watcher | i16 | 2 |
I32Watcher | i32 | 4 |
I64Watcher | i64 | 8 |
ISizeWatcher | isize | 4 |
U8Watcher | u8 | 1 |
U16Watcher | u16 | 2 |
U32Watcher | u32 | 4 |
U64Watcher | u64 | 8 |
USizeWatcher | usize | 4 |
F32Watcher | f32 | 4 |
F64Watcher | f64 | 8 |
moduleName— the name of the module whose base address the watcher will resolve each tick (e.g.'MyGame.exe'or'engine.dll').address— the offset from the module base at which the value is stored. The watcher adds this to the resolved base address before reading.
String Watcher
StringWatcher has a slightly different constructor because it needs to know how many bytes to read and which encoding the game uses:
length— the maximum byte length of the string buffer to read from memory.useUTF16— passtrueif the game stores strings as UTF-16 (common in .NET/Unity games); defaults tofalse(UTF-8).
StringWatcher.current and StringWatcher.old are both string values. The changed getter compares them with !==.
Declaring Watchers at Module Level
Watcher instances must live at module level so they persist across ticks. Initializing them insideupdate() would reset old to the initial value every single tick, making change detection useless.
levelWatcher.current > levelWatcher.old guards against the watcher triggering on a reset or rewind — you only split when the level number goes up.
Reacting to Specific Transitions
Because you have access to bothcurrent and old, you can match exact value pairs:
String Watcher Example
For games that store the current level or area name as a string in memory, useStringWatcher to detect room transitions:
Using Multiple Watchers
You can declare as many watchers as you need. Callupdate on each one in your update() function: