Written from a line-by-line source review; every example output is from a real run.
Introduction
The script_state plugin owns a single, globally living (running across the whole script's life) byte buffer, and exposes its address and size to the script. State based on the buffer can be shared between the main script thread and callbacks without resorting to file-level (global) variables — DominScript intentionally has none. The plugin works hand-in-hand with the blob plugin's RefFromPointer function: with the returned address and size, you can construct a ref-blob view onto the buffer.
Why it is needed
DominScript has no file-level or global variables: every piece of data lives in the scope of a function (or a struct). A callback therefore cannot directly carry the main thread's state. The script_state plugin solves this with a plugin-scoped storage: the main thread writes the state into the buffer, and the callback reads/writes the same memory through a ref-blob (using GetPointer + GetSize). Both sides see the same bytes.
Typical pattern with the blob plugin
Usually you describe the state with a struct and allocate the struct's bytes: `script_state.Allocate(structsize(AppState));`. Then in both the main thread and the callback you use `ref blob &S = blob.RefFromPointer(script_state.GetPointer(), script_state.GetSize()); S assign AppState;` for typed access. The plugin owns the buffer; the ref-blob is just a view onto it — the caller must therefore make sure no ref-blob is still in use before Free is called.
Loading the plugin
plugin "../plugins/print/PrintPlugin"; plugin "../plugins/script_state/ScriptStatePlugin"; plugin "../plugins/blob/BlobPlugin";
A typical flow — allocating and accessing script state
struct AppState packet
{
i32 width;
i32 height;
double angle;
}
i32 main()
{
script_state.Allocate(structsize(AppState));
{
ref blob &S = blob.RefFromPointer(script_state.GetPointer(),
script_state.GetSize());
S assign AppState;
S.width = 1280;
S.height = 720;
S.angle = 0.0;
}
// ... in callbacks the same ref blob can be recreated ...
script_state.Free();
return(0);
}What to know about every function (the basics)
The plugin owns one global buffer; at most one allocation exists at a time. A new Allocate call DROPS the old buffer (its contents are lost) and allocates a new one.
Allocate fills the buffer with ZERO bytes: every byte is 0 initially (via calloc). Typed structures thus start in a zeroed state.
Allocate(0) frees: it does not fail; it just drops the buffer and returns 0 (GetPointer and GetSize also become 0).
A negative size raises a runtime error.
GetPointer is an integer: the raw memory address of the buffer (converted to int64). It does NOT return a pointer type; usage goes through blob.RefFromPointer.
GetSize is the currently allocated size in bytes — 0 when unallocated.
An error (stopping the script) is caused by a wrong argument count or type, a negative size, and memory exhaustion.
How to read the signatures
size is the desired buffer size in bytes. GetPointer returns an integer memory address (as int64), which blob.RefFromPointer converts back into a ref-blob. The type after the -> arrow is the return type.
Lifecycle
Allocating and freeing the internal buffer. Only one buffer exists at a time; every new Allocate drops the existing one.
script_state.Allocate
script_state.Allocate(size) -> int
Allocates a zero-filled byte buffer of the given size. If a buffer already exists, it is dropped and a new one is allocated.
| Parameter | Type | Description |
|---|---|---|
| size | int | The desired buffer size in bytes (non-negative). 0 frees the existing buffer. |
The actual buffer size in bytes (size on a successful allocation, 0 after Allocate(0)). A failed allocation or a negative size raises a runtime error.
printf("%d\n", script_state.Allocate(64));
printf("%d\n", script_state.Allocate(128));
printf("%d\n", script_state.Allocate(0));64 128 0
For initializing the script state: with the structsize(...) operator you can size the allocation exactly to fit the state struct. Reallocation (with a different size) drops the old contents — if you need to keep them, save them into another blob first.
script_state.Free
script_state.Free() -> int
Frees the internal buffer. Safe to call even when unallocated.
This function takes no arguments.
Always 1.
script_state.Allocate(32);
printf("%d\n", script_state.Free());1
At the end of the script, or when replacing the script state. Before calling, make sure no ref-blob still points to the buffer — after Free the memory address is invalid.
Inspection
Querying the buffer's address and current size. With the returned values you can construct the ref-blob via blob.RefFromPointer.
script_state.GetPointer
script_state.GetPointer() -> int
Returns the raw memory address of the internal buffer as an integer (int64).
This function takes no arguments.
The buffer's address; 0 when unallocated.
script_state.Allocate(64);
printf("%d\n", (script_state.GetPointer() != 0));
script_state.Free();
printf("%d\n", script_state.GetPointer());1 0
The first argument of blob.RefFromPointer. It is not meant for pointer arithmetic on its own — only as input to the blob plugin's ref-blob constructor.
script_state.GetSize
script_state.GetSize() -> int
Returns the current size of the internal buffer in bytes.
This function takes no arguments.
The size in bytes; 0 when unallocated.
script_state.Allocate(128);
printf("%d\n", script_state.GetSize());
script_state.Free();
printf("%d\n", script_state.GetSize());128 0
The second argument of blob.RefFromPointer. Also useful on its own to check whether the script state is allocated (GetSize > 0).
Practical notes
What the script_state plugin is good for
Central script state: data the main thread and callbacks both need to access (animation angles, counters, configuration).
Sharing without global variables: DominScript has no file-level variables, so this plugin provides the callback-compatible state.
Combined with the blob plugin's RefFromPointer, any struct can be overlaid on the buffer — you get a typed state model.
Practical for resetting: Allocate(0) or Free frees the state in a single call.
Lifecycle rules
The plugin is designed for one buffer: a new Allocate drops the old one. If you need several independent state areas, it is more practical to organize the whole script state into ONE struct and allocate it with a single Allocate. After Free, GetPointer/GetSize return 0 and the previous address is invalid — any ref-blob still pointing to it becomes unusable.
Memory management
The plugin owns the buffer: Free or a new Allocate releases it. The script has only a view onto the memory (via blob.RefFromPointer), and the view owns nothing. On plugin shutdown the remaining buffer is freed automatically. The buffer starts at zero in every byte — due to calloc, there is no need for an explicit memset.
When this is NOT the right tool
If you need local, function-scoped state (not to be shared with callbacks), declaring the variable or blob in the function is simpler. If you want several parallel script-state areas, this plugin gives only one — organize a struct for several areas, or use your own blob.* allocations. If the data is only short-lived (does not outlive the function call), there is no need for this plugin.
Error handling
A wrong argument count or type, a negative size, and memory exhaustion are reported by the runtime as a runtime error, and the script stops. On a successful call, Allocate returns the actual size and Free always 1. GetPointer/GetSize called in the unallocated state return 0 — not an error, but a neutral state indicator.
The timer timing plugin
Monotonic clock, UTC epoch, formatted time, sleep, and scheduled callbacks — complete function reference