Your first plugin¶
This chapter walks through building a small plugin that exposes a Pawn native callable from the script.
Minimal skeleton¶
After completing the setup, replace the contents of
src/lib.rs with:
use samp::prelude::*;
use samp::{initialize_plugin, SampPlugin};
#[derive(SampPlugin, Default)]
struct MyPlugin;
initialize_plugin!(
type: MyPlugin,
natives: [],
);
That is already a valid plugin. #[derive(SampPlugin)] generates an
empty impl SampPlugin for MyPlugin {}, and the short
initialize_plugin!(type: T, ...) form uses Default::default() as the
constructor.
If the plugin needs initialization logic (logging, encoding, server tick, custom state), switch to the constructor-block form described in Plugin anatomy.
Adding a native¶
Natives are methods (or associated functions) annotated with #[native]:
use samp::prelude::*;
use samp::{native, initialize_plugin, SampPlugin};
#[derive(SampPlugin, Default)]
struct MyPlugin;
impl MyPlugin {
#[native(name = "RustSayHello")]
fn say_hello(&mut self, _amx: &Amx, name: &AmxString) -> AmxResult<bool> {
// AmxString implements Deref<Target = str> — &str methods are
// available without an extra allocation.
println!("Hello, {}!", &**name);
Ok(true)
}
}
initialize_plugin!(
type: MyPlugin,
natives: [MyPlugin::say_hello],
);
The
#[native]macro detects the&AmxStringparameter and injects the borrow automatically at the call site.args.next_arg()still produces the owned value; the function sees it through a reference.
Calling from Pawn¶
Declare the native in the Pawn script and call it normally:
The console prints Hello, World!.
What just happened¶
#[derive(SampPlugin)]emitsimpl SampPlugin for MyPlugin {}— every lifecycle method is left at its default (empty) implementation.initialize_plugin!(type: MyPlugin, ...)uses<MyPlugin as Default>::default()as the constructor.#[native(name = "RustSayHello")]generates theextern "C"wrapper the server expects, parses each argument viaAmxCell, catches panics, and converts theAmxResult<bool>return value into the integer expected by the AMX VM (true→1,false→0).AmxString::derefdecodes the underlying Pawn cells on first access (Windows-1252 by default, or whatever was configured via theencodingfeature) and caches the result in aOnceCell<String>, so repeated accesses are allocation-free.
Next steps¶
- Plugin anatomy — the full lifecycle and the
constructor-block form of
initialize_plugin!. - Natives — every option of
#[native]and the supported argument/return types. - Advanced examples — a richer plugin that uses
memcache, encoding, and a custom
fernlogger.