samp/lib.rs
1//! Rust toolkit for developing SA-MP plugins and native Open Multiplayer components.
2//!
3//! # Workspace structure
4//!
5//! - `samp` — main crate; re-exports SDK + codegen and exposes the API the plugin uses.
6//! - `samp-codegen` — proc macros (`#[native]`, `initialize_plugin!`,
7//! `#[derive(SampPlugin)]`) that generate FFI entry points and argument parsing.
8//! - `samp-sdk` — low-level bindings for the AMX VM (SA-MP) and for the component
9//! ABI (Open Multiplayer).
10//!
11//! # Minimal `Cargo.toml` setup
12//!
13//! ```toml
14//! [lib]
15//! crate-type = ["cdylib"]
16//!
17//! [dependencies]
18//! samp = { git = "https://github.com/NullSablex/rust-samp" }
19//! ```
20//!
21//! # Plugin example
22//!
23//! ```rust,ignore
24//! use samp::prelude::*;
25//! use samp::{native, initialize_plugin, SampPlugin};
26//!
27//! #[derive(SampPlugin, Default)]
28//! struct MyPlugin;
29//!
30//! impl MyPlugin {
31//! #[native(name = "Greet")]
32//! fn greet(&mut self, _amx: &Amx, name: &AmxString) -> AmxResult<bool> {
33//! if name.starts_with("Admin") {
34//! println!("[VIP] Welcome, {}!", &**name);
35//! } else {
36//! println!("Hello, {}!", &**name);
37//! }
38//! Ok(true)
39//! }
40//! }
41//!
42//! // Short form — default constructor via Default::default().
43//! initialize_plugin!(
44//! type: MyPlugin,
45//! natives: [MyPlugin::greet],
46//! );
47//!
48//! // Full form when there is setup in the constructor (logger, tick, etc):
49//! // initialize_plugin!(
50//! // natives: [MyPlugin::greet],
51//! // {
52//! // samp::plugin::enable_tick();
53//! // return MyPlugin;
54//! // }
55//! // );
56//! ```
57
58pub mod amx;
59pub mod events;
60#[doc(hidden)]
61pub mod interlayer;
62pub mod logger;
63pub(crate) mod macros;
64pub mod mainthread;
65pub mod omp_amx;
66#[doc(hidden)]
67pub mod panic_guard;
68pub mod pawn_include;
69pub mod plugin;
70pub(crate) mod runtime;
71#[cfg(test)]
72pub(crate) mod test_support;
73
74pub use samp_codegen::{event, initialize_plugin, native};
75
76/// Expands its input only when native Open Multiplayer support is compiled in —
77/// that is, without the `samp-only` feature.
78///
79/// `initialize_plugin!` wraps the Open Multiplayer entry point in it. The proc
80/// macro cannot tell which features this crate was built with; this macro is
81/// defined by the crate itself, once per case, so the answer is always this
82/// crate's.
83#[doc(hidden)]
84#[cfg(not(feature = "samp-only"))]
85#[macro_export]
86macro_rules! __omp_only {
87 ($($tokens:tt)*) => { $($tokens)* };
88}
89
90/// See the other definition: with `samp-only`, the input is dropped.
91#[doc(hidden)]
92#[cfg(feature = "samp-only")]
93#[macro_export]
94macro_rules! __omp_only {
95 ($($tokens:tt)*) => {};
96}
97
98/// Version of the `rust-samp` (`samp`) crate the plugin was compiled
99/// against. Useful for diagnostic natives that report the SDK build
100/// back to the gamemode (e.g. `MyPlugin_GetSdkVersion()`), bug reports
101/// and runtime dashboards.
102#[must_use]
103pub fn version() -> &'static str {
104 env!("CARGO_PKG_VERSION")
105}
106
107// Re-export so the generated macro does not leak the `log` dep into the user's Cargo.toml.
108#[doc(hidden)]
109pub use log;
110
111/// Derive macro that generates an empty `impl SampPlugin for T {}` for structs
112/// that do not need to customize any trait method. For structs with logic in
113/// `on_load`/`on_tick`/etc, declare `impl SampPlugin for T { ... }`
114/// manually instead of using the derive.
115pub use samp_codegen::SampPlugin;
116pub use samp_sdk::exec_public;
117pub use samp_sdk::{args, cell, consts, error, exports, raw};
118
119#[cfg(feature = "debug")]
120pub use samp_sdk::debug;
121
122#[cfg(feature = "encoding")]
123pub use samp_sdk::encoding;
124
125#[cfg(not(feature = "samp-only"))]
126pub use samp_sdk::omp;
127
128pub mod prelude {
129 //! Most commonly used imports in plugins.
130 pub use crate::amx::{Amx, AmxExt};
131 pub use crate::cell::{AmxCell, AmxString, Buffer, CellConvert, Ref, UnsizedBuffer};
132 pub use crate::error::AmxResult;
133 pub use crate::events::EventReturn;
134 pub use crate::plugin::SampPlugin;
135}
136
137/// Installs the SDK logger with defaults derived from the caller's
138/// `Cargo.toml`. Writes to `logs/{CARGO_PKG_NAME}.log` with size-based
139/// rotation (50 MB × 5 archives) and forwards every line to the server's
140/// own log prefixed with `[CARGO_PKG_NAME]`.
141///
142/// Returns `Result<(), samp::logger::InstallError>` — the most common
143/// failures are "already installed" (a second call in the same process)
144/// and "I/O" (the log directory could not be created).
145///
146/// # Example
147/// ```rust,ignore
148/// fn on_load(&mut self) {
149/// let _ = samp::enable_logger!();
150/// log::info!("ready");
151/// }
152/// ```
153#[macro_export]
154macro_rules! enable_logger {
155 () => {
156 $crate::enable_logger_with!($crate::logger::LoggerConfig::new(env!("CARGO_PKG_NAME")))
157 };
158}
159
160/// Installs the SDK logger with an explicit [`LoggerConfig`].
161///
162/// The macro still seeds the banner metadata from the caller's
163/// `CARGO_PKG_*` values before delegating to [`logger::install`], so the
164/// startup banner reports the user's plugin even when every other field
165/// is overridden.
166///
167/// [`LoggerConfig`]: crate::logger::LoggerConfig
168/// [`logger::install`]: crate::logger::install
169#[macro_export]
170macro_rules! enable_logger_with {
171 ($cfg:expr) => {{
172 $crate::logger::__set_banner_metadata($crate::logger::BannerMetadata::new(
173 env!("CARGO_PKG_NAME"),
174 env!("CARGO_PKG_VERSION"),
175 env!("CARGO_PKG_AUTHORS"),
176 env!("CARGO_PKG_REPOSITORY"),
177 ));
178 $crate::logger::install($cfg)
179 }};
180}
181
182#[cfg(test)]
183mod tests {
184 #[test]
185 fn version_matches_cargo_pkg_version() {
186 assert_eq!(super::version(), env!("CARGO_PKG_VERSION"));
187 assert!(!super::version().is_empty());
188 }
189}