pub struct Amx { /* private fields */ }Expand description
Wrapper over the raw *mut AMX and the exported function table.
Implementations§
Source§impl Amx
impl Amx
Sourcepub fn new(ptr: *mut AMX, fn_table: usize) -> Amx
pub fn new(ptr: *mut AMX, fn_table: usize) -> Amx
Builds the wrapper.
ptr is the pointer received in callbacks such as AmxLoad; fn_table
is the address resolved during plugin initialization (typically stored
in a global AtomicUsize read in Load() from
crate::consts::ServerData::AmxExports).
Sourcepub fn data_only(ptr: *mut AMX) -> Amx
pub fn data_only(ptr: *mut AMX) -> Amx
Wraps a VM for data-side access only, with no function table.
The register accessors and read_cell/write_cell/read_cells/
read_bytes/read_code resolve addresses straight from the AMX
struct, so they need no exported function table. Anything that calls
into the VM (register, exec, get_ref, allot…) does, and will
fail on an Amx built here.
Meant for a debug hook or a paused VM, where a plugin holds the pointer
but has no native call context — it states that intent instead of
passing a bare 0 as the function table.
Sourcepub fn register(&self, natives: &[AMX_NATIVE_INFO]) -> Result<(), AmxError>
pub fn register(&self, natives: &[AMX_NATIVE_INFO]) -> Result<(), AmxError>
Registers plugin natives in the VM via amx_Register.
Generally called in AmxLoad — the #[native] macro + initialize_plugin!
build the list automatically; only call manually from raw code.
§Errors
Propagates any AmxError returned by amx_Register — typically
AmxError::NotFound if a listed native is not declared in the script,
or VM state errors if called outside the load cycle.
Sourcepub fn exec(&self, index: AmxExecIdx) -> Result<i32, AmxError>
pub fn exec(&self, index: AmxExecIdx) -> Result<i32, AmxError>
Executes the public function identified by index in the VM.
Returns the Pawn return value (i32). Arguments must have been pushed
via push (in reverse order) and Allocator (for strings/arrays)
before this call.
§Errors
Propagates any AmxError from script execution — notably
Exit/Assert (Pawn aborted), StackError/StackLow/HeapLow
(stack or heap overflow), Divide, Native (a called native
returned an error) or Index if index does not match a valid function.
Sourcepub fn exec_public_scope<F, R>(
&self,
name: &str,
body: F,
) -> Result<R, AmxError>
pub fn exec_public_scope<F, R>( &self, name: &str, body: F, ) -> Result<R, AmxError>
Calls a public inside a managed Allocator scope — the escape hatch for
callbacks with output arrays, which the input-only exec_public!
macro cannot express.
Resolves name to its public index and opens an Allocator, then hands
both to body. Inside, allocate input/output buffers, push the
arguments (in reverse order), call exec, and read any output buffers
back — all before the scope closes and frees the heap. The scope also
rewinds the VM stack, so a mid-sequence push failure cannot unbalance it.
§Errors
AmxError::NotFound if the public does not exist; otherwise whatever
body returns (typically propagated from push/exec).
§Example
// Pawn: forward FillSquares(out[], size);
let squares = amx.exec_public_scope("FillSquares", |alloc, idx| {
let buf = alloc.allot_buffer(8)?; // output array
amx.push(8)?; // size (pushed first = last arg)
amx.push(&buf)?; // out[] (pushed last = first arg)
amx.exec(idx)?;
Ok(buf.as_slice().to_vec()) // read the array back before it frees
})?;Sourcepub fn find_native(&self, name: &str) -> Result<i32, AmxError>
pub fn find_native(&self, name: &str) -> Result<i32, AmxError>
Index of a native by name (resolved via amx_FindNative).
§Errors
AmxError::NotFound if name contains an interior NUL byte or if the
native is not registered in the VM.
Sourcepub fn call_native(&self, name: &str, params: &[i32]) -> Result<i32, AmxError>
pub fn call_native(&self, name: &str, params: &[i32]) -> Result<i32, AmxError>
Calls a native registered by another plugin in the same AMX.
SA-MP plugins inject their natives into every loaded AMX via
amx_Register, which writes a host function pointer into the
native’s entry inside the AMX_HEADER natives table. This helper
resolves the name through amx_FindNative, reads that function
pointer back, builds the params block in the AMX convention
(first cell = argc * sizeof(cell), then the arguments), and
invokes the native.
Integer arguments are passed as their i32 value. Floats are
passed bit-cast to i32 (use f32::to_bits then
i32::from_ne_bytes on to_ne_bytes, or f32::to_bits() as i32).
String and array arguments are AMX cell addresses returned by
Allocator::allot_string/Allocator::allot_buffer — same
marshalling as for exec_public.
§Example
// Calling Streamer_CreateDynamicObject from a Rust plugin
fn on_amx_load(&mut self, amx: &Amx) -> AmxResult<()> {
let model_id: i32 = 1337;
#[allow(clippy::cast_possible_wrap)]
let x = 100.0_f32.to_bits() as i32;
let y = 200.0_f32.to_bits() as i32;
let z = 10.0_f32.to_bits() as i32;
let object_id = amx.call_native(
"Streamer_CreateDynamicObject",
&[model_id, x, y, z, 0, 0, 0],
)?;
log::info!("created dynamic object id={object_id}");
Ok(())
}§Errors
AmxError::NotFoundifnamecontains an interior NUL byte, the native is not registered, or its address is still zero (registered name but no host pointer attached).AmxError::MemoryAccessif the AMX header cannot be read.AmxError::Indexif the resolved index is out of range for the natives table reported by the AMX header.- Any
AmxErrorpropagated from the called native viaamx.error(re-raised by the caller throughamx_try!).
Sourcepub fn find_public(&self, name: &str) -> Result<AmxExecIdx, AmxError>
pub fn find_public(&self, name: &str) -> Result<AmxExecIdx, AmxError>
Index of a public function by name — pass the result to exec.
use samp_sdk::amx::Amx;
use samp_sdk::error::AmxResult;
fn has_on_player_connect(amx: &Amx) -> AmxResult<bool> {
let idx = amx.find_public("OnPlayerConnect")?;
Ok(i32::from(idx) >= 0)
}§Errors
AmxError::NotFound if name contains an interior NUL byte or if the
public function is not declared in the Pawn script.
Sourcepub fn find_pubvar<T>(&self, name: &str) -> Result<Ref<'_, T>, AmxError>where
T: AmxPrimitive,
pub fn find_pubvar<T>(&self, name: &str) -> Result<Ref<'_, T>, AmxError>where
T: AmxPrimitive,
Ref<T> pointing to a public variable declared in the Pawn script.
let version = amx.find_pubvar::<f32>("my_plugin_version")?;
// outdated
if *version < 1.0 { }§Errors
AmxError::NotFound if name contains an interior NUL byte or if the
pubvar is not declared. AmxError::MemoryAccess if the address returned
by the VM is invalid.
Sourcepub fn opcode_table(&self, count: usize) -> Option<Vec<usize>>
pub fn opcode_table(&self, count: usize) -> Option<Vec<usize>>
Returns the VM’s opcode dispatch table (amx_opcodelist): count raw
label addresses, one per opcode, in opcode order.
On a server built with computed-goto threading (GCC/Clang, the SA-MP and
open.mp builds), the loader rewrites each opcode in the code segment to the
address of its handler label, so a byte read with read_code yields a
pointer, not the opcode number. Inverting this table (address → opcode)
lets a debugger recover the real opcode at cip. The table is fetched the
way the loader itself does it — set the BROWSE flag and call amx_Exec
with index 0, which returns &amx_opcodelist instead of running code.
count is the number of opcodes the caller expects (OP_NUM_OPCODES);
the SDK does not hardcode the VM’s opcode count. Returns None only when
the table cannot be obtained (null VM/table).
The AMX_FLAG_RELOC header bit is intentionally not consulted: it is
set by the loader in the file header and may not yet be visible at
AmxLoad time, even though the dispatch table is already available. A
non-computed-goto VM would return a table whose addresses simply never
match a real opcode, so inverting it is harmless (the consumer finds no
match and treats the code value as a raw opcode).
Sourcepub fn allocator(&self) -> Allocator<'_>
pub fn allocator(&self) -> Allocator<'_>
Creates an Allocator bound to this Amx.
All memory allocated via Allocator::allot/Allocator::allot_buffer/
Allocator::allot_string is released automatically when the
Allocator goes out of scope (Drop). Keep it alive while using the
returned references.
Sourcepub fn amx(&self) -> Option<NonNull<AMX>>
pub fn amx(&self) -> Option<NonNull<AMX>>
Raw pointer to the AMX (non-null) or None if constructed with null.
Sourcepub fn header(&self) -> Option<NonNull<AMX_HEADER>>
pub fn header(&self) -> Option<NonNull<AMX_HEADER>>
Raw pointer to the AMX_HEADER of the loaded .amx.
Sourcepub fn cip(&self) -> Option<u32>
pub fn cip(&self) -> Option<u32>
Current instruction pointer (cip) — a code-segment offset in a debug
hook. Read as u32.
Sourcepub fn frame(&self) -> Option<i32>
pub fn frame(&self) -> Option<i32>
Current frame pointer (frm); local/argument symbols are addressed
relative to it.
Sourcepub fn stp(&self) -> Option<i32>
pub fn stp(&self) -> Option<i32>
Top of the stack (stp) — the upper bound of the data address space.
Sourcepub fn hlw(&self) -> Option<i32>
pub fn hlw(&self) -> Option<i32>
Heap low-water mark (hlw) — the bottom of the heap segment. The heap
grows upward from here; releasing it below hlw is what the VM reports as
AMX_ERR_HEAPLOW. A debugger reads it in a debug hook to detect a heap
underflow before the VM aborts.
Sourcepub fn pri(&self) -> Option<i32>
pub fn pri(&self) -> Option<i32>
Primary register (pri) — the VM’s main accumulator. In a debug hook it
holds the operand the next instruction will act on; e.g. for OP_BOUNDS
it is the index being range-checked.
Sourcepub fn alt(&self) -> Option<i32>
pub fn alt(&self) -> Option<i32>
Alternate register (alt) — the VM’s secondary accumulator. For the
division opcodes (OP_DIV/OP_SDIV) it holds the divisor, so reading it
in a debug hook lets a debugger detect a divide-by-zero before it aborts.
Sourcepub fn read_code(&self, offset: u32) -> Option<i32>
pub fn read_code(&self, offset: u32) -> Option<i32>
Reads a 32-bit cell from the code segment at offset (a code-segment
offset, like cip). Returns None when the VM pointer is null or the
offset is outside the code segment [0, header.dat - header.cod).
The code segment is read-only and laid out as base + header.cod; this is
the counterpart of read_cell for instructions. A
debugger uses it to decode the opcode at cip inside a debug hook (e.g. to
catch a runtime error before the VM aborts). Reads byte-wise (no alignment
assumption), since the AMX_HEADER is packed.
Sourcepub fn read_cell(&self, addr: i32) -> Option<i32>
pub fn read_cell(&self, addr: i32) -> Option<i32>
Reads a 32-bit cell from the data segment at addr, validating bounds
like amx_GetAddr. Returns None if the address is inaccessible.
Reads byte-wise (no alignment assumption). Usable from a debug hook.
Sourcepub fn read_cells(&self, addr: i32, count: usize) -> Option<Vec<i32>>
pub fn read_cells(&self, addr: i32, count: usize) -> Option<Vec<i32>>
Reads up to count consecutive cells starting at addr, validating
each one like read_cell.
Stops early and returns what it read when an address becomes
inaccessible — the natural case at the end of the data segment. None
only when addr itself is inaccessible.
Unlike get_ref-based access (Buffer, AmxString),
this needs no function table, so it works inside a debug hook.
Sourcepub fn read_bytes(&self, addr: i32, len: usize) -> Option<Vec<u8>>
pub fn read_bytes(&self, addr: i32, len: usize) -> Option<Vec<u8>>
Reads up to len raw bytes of the data segment starting at addr, in
the VM’s native byte order — the backing read for a hex view.
addr needs no alignment: the read starts at the enclosing cell and the
leading bytes are trimmed. Like read_cells, it
stops early at the first inaccessible address, so the result may be
shorter than len; None only when addr itself is inaccessible.
Sourcepub fn write_cell(&self, addr: i32, value: i32) -> bool
pub fn write_cell(&self, addr: i32, value: i32) -> bool
Writes a 32-bit cell to the data segment at addr, validating bounds
like amx_GetAddr. Returns false if the address is inaccessible.
Writes byte-wise (no alignment assumption). Usable from a debug hook to edit a variable while the VM is paused.
Sourcepub fn install_debug_hook(&self, cb: extern "C" fn(*mut AMX) -> i32)
pub fn install_debug_hook(&self, cb: extern "C" fn(*mut AMX) -> i32)
Installs a debug hook callback into this VM (amx->debug = cb), the
equivalent of amx_SetDebugHook. The VM then calls cb on every line,
provided the .amx was compiled with -d2/-d3.
The callback runs on the VM thread and crosses the FFI boundary, so it must never unwind (no panics).
Sourcepub fn remove_debug_hook(&self)
pub fn remove_debug_hook(&self)
Removes a previously installed debug hook, restoring amx->debug to a
no-op callback that returns AMX_ERR_NONE.