This chapter walks through building a small plugin that exposes a Pawn native callable from the script.
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.
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.
Declare the native in the Pawn script and call it normally:
native RustSayHello(const name[]);
public OnGameModeInit()
{
RustSayHello("World");
return 1;
}The console prints Hello, World!.
#[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.
Add one call inside on_load and every log::info! / log::warn! /
log::error! from this plugin starts going to logs/my-plugin.log and
to the server console with the plugin name as a prefix:
impl SampPlugin for MyPlugin {
fn on_load(&mut self) {
let _ = samp::enable_logger!();
log::info!("plugin loaded");
}
}That covers most plugins. To customize the filename, prefix, file format, rotation policy or banner, see the dedicated Logging chapter.
- Plugin anatomy — the full lifecycle and the
constructor-block form of
initialize_plugin!. - Natives — every option of
#[native]and the supported argument/return types. - Logging — the turnkey logger, custom formats, rotation, banner customization, and runtime level adjustment.
- Advanced examples — a richer plugin that uses
memcache, encoding, and a custom
fernlogger.