Skip to main content

samp_sdk/
exports.rs

1//! Typed access to the `amx_*` functions exported by the server.
2//!
3//! The server exposes `amx_Exports` (passed in `Load()`) as a flat table of
4//! pointers, indexed by the values of [`Exports`]. Each function has a
5//! zero-sized marker type (generated by `impl_export!`) that carries the
6//! corresponding offset in [`Export::OFFSET`] and the function type in [`Export::Output`].
7//!
8//! Typical usage: `Exec::try_from_table(fn_table)` returns the typed pointer,
9//! or `None` when the server left that slot empty. Resolving on demand avoids
10//! storing all 44 pointers in [`crate::amx::Amx`].
11
12use crate::raw::functions;
13
14macro_rules! impl_export {
15    ($name:ident) => {
16        pub struct $name;
17
18        impl Export for $name {
19            type Output = functions::$name;
20            const OFFSET: isize = Exports::$name as isize;
21        }
22    };
23}
24
25/// Retrieves a typed pointer from the `amx_Exports` table.
26pub trait Export {
27    /// Type of the exported function — alias declared in [`crate::raw::functions`].
28    type Output;
29
30    /// Offset of this function in the table. Values correspond 1:1 to [`Exports`].
31    const OFFSET: isize;
32
33    /// Reads the pointer at offset `OFFSET` of the `fn_table` table.
34    ///
35    /// `None` when the table is null or that slot is empty, which means a server
36    /// that did not fill it. It is not this crate's place to end the process over
37    /// that: the caller turns it into an error the plugin can report.
38    #[inline(always)]
39    fn try_from_table(fn_table: usize) -> Option<Self::Output> {
40        if fn_table == 0 {
41            return None;
42        }
43        let table = fn_table as *const usize;
44
45        unsafe {
46            let ptr = table.offset(Self::OFFSET);
47            // Reading the slot anyway would call through a null pointer.
48            if ptr.read() == 0 {
49                return None;
50            }
51            Some(ptr.cast::<Self::Output>().read())
52        }
53    }
54
55    /// Reads the pointer at offset `OFFSET` of the `fn_table` table.
56    ///
57    /// # Panics
58    /// When the table is null or the slot is empty — which, inside a server,
59    /// ends its process.
60    #[deprecated(
61        since = "3.6.0",
62        note = "panics on a table the server did not fill; use `try_from_table`"
63    )]
64    #[inline(always)]
65    fn from_table(fn_table: usize) -> Self::Output {
66        Self::try_from_table(fn_table).expect("from_table(): null table or empty slot")
67    }
68}
69
70impl_export!(Align16);
71impl_export!(Align32);
72impl_export!(Allot);
73impl_export!(Callback);
74impl_export!(Cleanup);
75impl_export!(Clone);
76impl_export!(Exec);
77impl_export!(FindNative);
78impl_export!(FindPublic);
79impl_export!(FindPubVar);
80impl_export!(FindTagId);
81impl_export!(Flags);
82impl_export!(GetAddr);
83impl_export!(GetNative);
84impl_export!(GetPublic);
85impl_export!(GetPubVar);
86impl_export!(GetString);
87impl_export!(GetTag);
88impl_export!(GetUserData);
89impl_export!(Init);
90impl_export!(InitJIT);
91impl_export!(MemInfo);
92impl_export!(NameLength);
93impl_export!(NativeInfo);
94impl_export!(NumNatives);
95impl_export!(NumPublics);
96impl_export!(NumPubVars);
97impl_export!(NumTags);
98impl_export!(Push);
99impl_export!(PushArray);
100impl_export!(PushString);
101impl_export!(RaiseError);
102impl_export!(Register);
103impl_export!(Release);
104impl_export!(SetCallback);
105impl_export!(SetDebugHook);
106impl_export!(SetString);
107impl_export!(SetUserData);
108impl_export!(StrLen);
109impl_export!(UTF8Check);
110impl_export!(UTF8Get);
111impl_export!(UTF8Len);
112impl_export!(UTF8Put);
113
114// Open Multiplayer only — see the note on [`Exports`].
115impl_export!(PushStringLen);
116impl_export!(SetStringLen);
117impl_export!(Swap16);
118impl_export!(Swap32);
119impl_export!(Swap64);
120impl_export!(GetNativeByIndex);
121impl_export!(MakeAddr);
122impl_export!(StrSize);
123
124/// Indices of the `amx_*` functions in the server's `amx_Exports` table.
125///
126/// The order is fixed by the SA-MP ABI — do not reorder.
127///
128/// Entries `[0..=43]` exist on both servers. Entries `[44..=51]` exist **only**
129/// in the 52-slot table open.mp's Pawn component returns from
130/// `getAmxFunctions()`; the SA-MP table ends at `[43]`, so resolving one of
131/// them against a SA-MP table reads past its end. The `samp` crate exposes
132/// them through `AmxOmpExt`, which checks the running mode first.
133#[derive(Debug, Clone, Copy, PartialEq)]
134pub enum Exports {
135    Align16 = 0,
136    Align32 = 1,
137    Align64 = 2,
138    Allot = 3,
139    Callback = 4,
140    Cleanup = 5,
141    Clone = 6,
142    Exec = 7,
143    FindNative = 8,
144    FindPublic = 9,
145    FindPubVar = 10,
146    FindTagId = 11,
147    Flags = 12,
148    GetAddr = 13,
149    GetNative = 14,
150    GetPublic = 15,
151    GetPubVar = 16,
152    GetString = 17,
153    GetTag = 18,
154    GetUserData = 19,
155    Init = 20,
156    InitJIT = 21,
157    MemInfo = 22,
158    NameLength = 23,
159    NativeInfo = 24,
160    NumNatives = 25,
161    NumPublics = 26,
162    NumPubVars = 27,
163    NumTags = 28,
164    Push = 29,
165    PushArray = 30,
166    PushString = 31,
167    RaiseError = 32,
168    Register = 33,
169    Release = 34,
170    SetCallback = 35,
171    SetDebugHook = 36,
172    SetString = 37,
173    SetUserData = 38,
174    StrLen = 39,
175    UTF8Check = 40,
176    UTF8Get = 41,
177    UTF8Len = 42,
178    UTF8Put = 43,
179    // --- Open Multiplayer only ---
180    PushStringLen = 44,
181    SetStringLen = 45,
182    Swap16 = 46,
183    Swap32 = 47,
184    Swap64 = 48,
185    GetNativeByIndex = 49,
186    MakeAddr = 50,
187    StrSize = 51,
188}
189
190impl From<Exports> for isize {
191    fn from(exports: Exports) -> isize {
192        exports as isize
193    }
194}