Skip to main content

samp_sdk/
consts.rs

1//! SA-MP ABI constants: plugin flags, offsets of the server's exports table
2//! and internal AMX VM flags. Values identical to those in the C header.
3
4use bitflags::bitflags;
5
6bitflags! {
7    /// Capabilities advertised by the plugin in the `Supports()` export.
8    ///
9    /// The server reads this bitfield when loading the `.dll`/`.so` and decides
10    /// which callbacks to fire (e.g. `ProcessTick` is only called if `PROCESS_TICK`
11    /// is present).
12    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13    pub struct Supports: u32 {
14        const VERSION = 512;
15        const AMX_NATIVES = 0x10000;
16        const PROCESS_TICK = 0x20000;
17    }
18}
19
20/// Offsets within the `ppData` table passed to `Load(void**)`.
21///
22/// The server exposes pointers to utility functions (`logprintf`) and to the
23/// AMX function table at these fixed indices.
24#[derive(Debug, Clone, Copy, PartialEq)]
25pub enum ServerData {
26    Logprintf = 0,
27    AmxExports = 16,
28    CallPublicFs = 17,
29    CallPublicGm = 18,
30}
31
32impl From<ServerData> for isize {
33    fn from(data: ServerData) -> isize {
34        data as isize
35    }
36}
37
38bitflags! {
39    /// Flags from the `.amx` header — `AMX::flags` in the C header.
40    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
41    pub struct AmxFlags: u16 {
42        const DEBUG = 0x02;
43        const COMPACT = 0x04;
44        const BYTEOPC = 0x08;
45        const NOCHECKS = 0x10;
46        const NTVREG = 0x1000;
47        const JITC = 0x2000;
48        const BROWSE = 0x4000;
49        const RELOC = 0x8000;
50    }
51}
52
53/// Identifier of the public function to execute via `amx_Exec`.
54///
55/// The values `-1` (entry point `main`) and `-2` (continue suspended execution)
56/// are sentinels reserved by the VM. Indices >= 0 refer to public functions
57/// resolved via `amx_FindPublic`.
58#[derive(Debug, Clone, Copy, PartialEq)]
59pub enum AmxExecIdx {
60    Main,
61    Continue,
62    UserDef(i32),
63}
64
65impl From<AmxExecIdx> for i32 {
66    fn from(value: AmxExecIdx) -> i32 {
67        match value {
68            AmxExecIdx::Main => -1,
69            AmxExecIdx::Continue => -2,
70            AmxExecIdx::UserDef(idx) => idx,
71        }
72    }
73}
74
75impl From<i32> for AmxExecIdx {
76    fn from(idx: i32) -> AmxExecIdx {
77        match idx {
78            -1 => AmxExecIdx::Main,
79            -2 => AmxExecIdx::Continue,
80            idx => AmxExecIdx::UserDef(idx),
81        }
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn amx_exec_idx_main_roundtrip() {
91        assert_eq!(i32::from(AmxExecIdx::Main), -1);
92        assert_eq!(AmxExecIdx::from(-1), AmxExecIdx::Main);
93    }
94
95    #[test]
96    fn amx_exec_idx_continue_roundtrip() {
97        assert_eq!(i32::from(AmxExecIdx::Continue), -2);
98        assert_eq!(AmxExecIdx::from(-2), AmxExecIdx::Continue);
99    }
100
101    #[test]
102    fn amx_exec_idx_userdef_roundtrip() {
103        for idx in [0, 1, 10, 100, i32::MAX] {
104            assert_eq!(AmxExecIdx::from(idx), AmxExecIdx::UserDef(idx));
105            assert_eq!(i32::from(AmxExecIdx::UserDef(idx)), idx);
106        }
107    }
108
109    #[test]
110    fn server_data_offsets() {
111        assert_eq!(isize::from(ServerData::Logprintf), 0);
112        assert_eq!(isize::from(ServerData::AmxExports), 16);
113        assert_eq!(isize::from(ServerData::CallPublicFs), 17);
114        assert_eq!(isize::from(ServerData::CallPublicGm), 18);
115    }
116
117    #[test]
118    fn supports_flags_combine() {
119        let flags = Supports::VERSION | Supports::AMX_NATIVES;
120        assert!(flags.contains(Supports::VERSION));
121        assert!(flags.contains(Supports::AMX_NATIVES));
122        assert!(!flags.contains(Supports::PROCESS_TICK));
123    }
124
125    #[test]
126    fn supports_with_process_tick() {
127        let flags = Supports::VERSION | Supports::AMX_NATIVES | Supports::PROCESS_TICK;
128        assert!(flags.contains(Supports::PROCESS_TICK));
129    }
130
131    #[test]
132    fn amx_flags_combine() {
133        let flags = AmxFlags::DEBUG | AmxFlags::COMPACT;
134        assert!(flags.contains(AmxFlags::DEBUG));
135        assert!(flags.contains(AmxFlags::COMPACT));
136        assert!(!flags.contains(AmxFlags::JITC));
137    }
138
139    #[test]
140    fn amx_flags_empty() {
141        let flags = AmxFlags::empty();
142        assert!(!flags.contains(AmxFlags::DEBUG));
143        assert!(flags.is_empty());
144    }
145}