Releases: NullSablex/rust-samp
v3.1.0
Headline: turnkey logger — samp::enable_logger!() installs a complete
per-plugin logging pipeline (file under logs/, size-based rotation
into logs/archive/, prefix derived from CARGO_PKG_NAME, startup
banner, runtime-adjustable level) in a single call. The previous
samp::plugin::logger() DIY path stays unchanged for advanced cases.
v3.1.0 is also the first version available on crates.io —
rust-samp,
rust-samp-sdk and
rust-samp-codegen.
Earlier releases (v3.0.0 and the entire v2.x line) are not published to
the registry; plugins targeting those versions must keep using a git
dependency. The library names (samp, samp_sdk, samp_codegen)
are unchanged; only the package names differ on the registry to
avoid colliding with the upstream samp-rs fork.
Crate versions
rust-samp(libsamp): 3.0.0 → 3.1.0rust-samp-sdk(libsamp_sdk): 3.0.0 — unchanged (metadata only)rust-samp-codegen(libsamp_codegen): 1.3.0 — unchanged (metadata only)
Installation
[lib]
crate-type = ["cdylib"]
[dependencies]
samp = { package = "rust-samp", version = "3" }
log = "0.4"The package is published as rust-samp; the alias keeps the
source-level use samp::prelude::*; imports unchanged. Git consumers
do not need to update anything — both names continue to resolve to the
same library.
New features
- Turnkey logger — new module
samp::loggerplus the
samp::enable_logger!()andsamp::enable_logger_with!(cfg)macros.
The macros capture the caller'sCARGO_PKG_*at compile time and
install alog::Logimplementation that routes through both the
server's log sink and a per-plugin file. LoggerConfigbuilder — every aspect of the pipeline is
configurable through fluent setters:directory,filename,
prefix,level,also_to_server,banner,file_format,
server_format,rotation_size_mb,rotation_keep,
rotation_no_cleanup,no_rotation,no_banner,banner_with.- Format templates —
file_formatandserver_formataccept
{timestamp},{level},{message}and (server-only){prefix}
placeholders with optional alignment specifiers ({level:>5},
{level:<5},{level:^5}). Unknown placeholders pass through
verbatim so typos are visible. - Banner modes —
BannerMode::Default(5-line banner from
CARGO_PKG_*),BannerMode::Off, andBannerMode::Custom(closure
receivingBannerMetadataand returning the lines to render). - Size-based rotation — when the active file passes
rotation_size_mb(default 50 MB), it is renamed into
{directory}/archive/{filename}.{N}and a fresh active file is
opened. Two strategies are available:- Append-style (default): every rotation uses the next free
index, archives are never deleted by the SDK, the dev keeps
full control over cleanup. The archive folder is created lazily on
the first rotation; the next index survives restarts (rescanned at
install time). - Shift-style — opt-in via
rotation_keep(N):.log.Nis
deleted, every other archive shifts down, active becomes.log.1.
Disk footprint becomes(keep + 1) * rotation_size_mb.
- Append-style (default): every rotation uses the next free
- Runtime level adjustment —
samp::logger::set_level(...)and
samp::logger::level()let plugins expose a Pawn-side knob for log
verbosity (e.g. aMyPlugin_SetLogLevel(level)native). InstallError—Display+Errorwithsource()exposing
the innerstd::io::Errorfor theIovariant.
Migrating to the turnkey logger
The previous handcrafted fern::Dispatch pattern keeps working —
adoption is optional.
| Before (v3.0.0) | After (v3.1.0) |
|---|---|
fern::Dispatch::new().level(...).chain(samp::plugin::logger()).chain(fern::log_file(...)?).format(...).apply()?; |
samp::enable_logger!() inside on_load |
Hand-built prefix string format_args!("[my-plugin][{}]: {}", record.level(), message) |
Automatic [CARGO_PKG_NAME] prefix |
| Manually managing the log filename | Default logs/{CARGO_PKG_NAME}.log |
External logrotate setup for file growth |
Built-in 50 MB rotation into logs/archive/ |
Pawn-side SetLogLevel(level) native backed by a static AtomicI32 |
samp::logger::set_level(LevelFilter) direct |
See docs/logging.md for the full reference,
including the three layers (turnkey / LoggerConfig / DIY with fern),
format placeholders, rotation modes, and runtime tuning.
Packaging (crates.io)
The three workspace crates now have crates.io-ready metadata
(description, keywords, categories, rust-version, per-crate
README.md) and centralized shared fields in [workspace.package].
-
Package names:
rust-samp,rust-samp-sdk,rust-samp-codegen.
The upstreamsamp/samp-sdk/samp-codegennames on crates.io
belong to the originalsamp-rsauthor and are not the publication
target of this fork. -
Library names: unchanged —
samp,samp_sdk,samp_codegen.
Existinguse samp::prelude::*;keeps compiling. -
Crates.io consumers add the package alias to their
Cargo.toml:[dependencies] samp = { package = "rust-samp", version = "3" }
-
Git consumers (
samp = { git = "..." }) need no change — the
workspace exposes both names.
Build
- MSRV bumped to Rust 1.88 (was 1.85). Required by stable
i32::cast_unsigned/u32::cast_signed(used internally for AMX
cell bit conversions) and by the patchedtime 0.3.47/time-core 0.1.8/time-macros 0.2.27. Declared via
[workspace.package].rust-version = "1.88". - New transitive dependency —
time = "0.3.47"(features
local-offset,formatting,macros) is pulled in by the turnkey
logger for timestamp formatting. Thechronocrate is not added.
The minimum is pinned to0.3.47to pick up the fix for
RUSTSEC-2026-0009
(DoS via stack exhaustion, medium severity).
CI / release infrastructure
- Release notes are now auto-assembled — workflow
.github/workflows/release.yml
combines the curated CHANGELOG section with the GitHub-native
releases/generate-notesAPI output, keeping the "New Contributors"
block and the "Full Changelog" comparison link while dropping the
redundant## What's Changedheader. - Crates.io publication is wired into the release workflow — on
v*tag push (and manualworkflow_dispatchwith adry_run
input), the workflow validates the workspace, then publishes
rust-samp-sdk→rust-samp-codegen→rust-sampin dependency
order with a 30 s sleep between steps. Eachcargo publishstep
gracefully skips when the version is already on crates.io, so a
patch release that bumps only one crate goes through unattended. - Bench jobs were updated to reference
rust-samp-sdkinstead of
the pre-renamesamp-sdkpackage id. - Release-drafter template no longer emits the duplicated
## What's Changedheading at the top of release notes.
Documentation
docs/logging.md— rewritten end-to-end (139 → 413 lines).
Covers the three layers, format placeholders and alignment specs,
banner modes, rotation strategies, runtime level adjustment, and a
pitfalls section.docs/api-reference.md— newsamp::loggersection with the
full builder signature, error type, and placeholder reference.docs/migration.md— new v3.0.0 → v3.1.0 section with the
before/after migration table and crates.io adoption notes.docs/first-plugin.md— new "Enabling logging" section showing
the one-liner insideon_load.docs/plugin-anatomy.md— explicit note thatenable_logger!
belongs inon_load, not the constructor block (server's log sink
is not connected yet during construction).docs/index.md— the integrated-logging bullet now describes
the turnkey path; workspace table reflects the newsampversion.docs/advanced-examples.md— theexamples/countersnippet
matches the source change (usessamp::enable_logger!()instead of
the handcraftedfern::Dispatch).- Dual-availability sweep —
README.md,migration.md,
docs/setup.md,docs/encoding.mdanddocs/migration.mdnow show
both installation paths side-by-side (crates.io for v3.1.0+, git for
any version including v3.0.0 and earlier) with a consistent
package = "rust-samp"snippet. The workspace version table in
README.mdwas bumped to reflect the newsamp3.1.0.
Examples
examples/counter(1.0.0 → 1.1.0) —on_loadnow calls
samp::enable_logger!(); theferndependency was dropped.examples/hello(1.0.0 → 1.0.1),examples/advanced(1.1.0 →
1.1.1) — switched to thepackage = "rust-samp"alias so the path
dependency matches the published name. No source changes.
Repository housekeeping
ROADMAP.mdmoved out of version control (now under.gitignore)
— it stays as a local working document for the maintainer and is no
longer shipped to crates.io tarballs or GitHub..github/CODEOWNERSadded.CNAMEremoved (project pages are served from the default
nullsablex.github.io/rust-samp/URL).
Code quality
- Clippy
-D warningsand `-W clippy::pedanti...
v3.0.0
Compared to v2.2.0 (2026/03/15).
Headline: native Open Multiplayer component ABI implemented in pure
Rust (Itanium and MSVC); a single binary now works as a SA-MP
plugin and as a first-class Open Multiplayer component, with no extra
configuration.
Migrating from v2.x (or from the upstream samp-rs fork)
This release contains breaking changes. A plugin written against
v2.x will not compile against v3.0.0 without edits. Minimum diff:
| Before (v2.x / upstream) | After (v3.0.0) |
|---|---|
fn process_tick(&mut self) { … } |
fn on_tick(&mut self, _ctx: TickContext) { … } |
samp::plugin::enable_process_tick() |
samp::plugin::enable_tick() (or enable_tick_with(TickConfig)) |
samp::cell::string::put_in_buffer(buf, s)? |
buf.write_str(s)? / unsized.write_str(size, s)? (put_in_buffer is pub(crate) now) |
samp::raw::functions::Logprintf (variadic) |
Same path, now extern "C" fn(*const i8) — the SDK formats in Rust and passes a single C string |
example-hello/ / example-counter/ / plugin-example/ |
examples/hello/ / examples/counter/ / examples/advanced/ |
Open Multiplayer support comes turned on by default — the build
produces both the SA-MP exports and the ComponentEntryPoint. To
keep the v2.x behavior unchanged, enable the new samp-only feature:
samp = { git = "...", tag = "v3.0.0", features = ["samp-only"] }Two new requirements that affect builds:
- The workspace's
[profile.release]addslto = "thin",
codegen-units = 1,strip = true. Override in your own
Cargo.tomlif you need otherwise. - The i686 target is now strictly required at compile time
(OmpComponenthasconst _layout asserts). Set
target = "i686-unknown-linux-gnu"in.cargo/config.tomlif you
were relying on the host target picking up automatically.
The full step-by-step walkthrough lives in
docs/migration.md — including the new
TickConfig knobs (sa_mp_only(), omp_only(Duration), custom
omp_interval) and how to choose between them.
Crate versions
samp: 2.2.0 → 3.0.0samp-sdk: 2.2.0 → 3.0.0samp-codegen: 1.2.0 → 1.3.0
Breaking changes
SampPlugin::process_tickreplaced by
SampPlugin::on_tick(&mut self, ctx: TickContext)— unified
callback that fires on both servers. Cadence is the server's main
loop on SA-MP and the SDK-ownedITimersComponenttimer on native
Open Multiplayer (interval configurable).TickContext::source
reports the origin (TickSource::SaMp/
TickSource::OmpTimer);TickContext::elapsedis the
wall-clock interval since the previous dispatch.samp::plugin::enable_process_tickreplaced by
samp::plugin::enable_tick()(default config) and
samp::plugin::enable_tick_with(config: TickConfig)(explicit
per-server toggle + Open Multiplayer interval).samp::cell::string::put_in_bufferis nowpub(crate)(was
pub). The public API for writing strings isBuffer::write_str
andUnsizedBuffer::write_str.samp_sdk::raw::functions::Logprintfsignature changed from
variadicextern "C" fn(*const i8, ...)to fixed-arity
extern "C" fn(*const i8)— the SDK formats the message in Rust
and passes a single C string, matching whatlogprintf("%s", msg)
does at the ABI level.- Example crates renamed:
example-hello/→examples/hello/,
example-counter/→examples/counter/,
plugin-example/→examples/advanced/. Workspace members
updated accordingly.
Added — native Open Multiplayer support
samp_sdk::omp— new top-level module with eight submodules:component—OmpComponent,IComponentVTable,
IUIDProviderVTable, opaque types (ICore,IComponentList,
ILogger,IEarlyConfig), default vtable implementations for
both ABIs.component_api—OmpComponentHandletrait, generic
component_name<T>()/component_version<T>()helpers.core—LogLevel,core_print_ln,core_log_ln,
core_print_ln_u8,core_log_ln_u8.events—PawnEventHandler,PawnEventHandlerVTable.server—PAWN_COMPONENT_UID,NUM_AMX_FUNCS,
PawnComponent,ServerComponentList,ServerComponent,
ServerPawnComponent,IEventDispatcherPawn,IPawnScript,
AmxFunctionTable, plus the free functions
query_component,add_pawn_event_handler,
remove_pawn_event_handler,get_pawn_event_dispatcher,
get_amx_from_script,get_amx_functions.timers—TIMERS_COMPONENT_UID,TimersComponent,
ITimersComponent,ITimer,TimerHandlerVTable,
TimerTimeOutHandler,create_repeating_timer,kill_timer,
query_timers_component.types—UID,SemanticVersion(withnewand
with_prerel),StringView(withas_str,try_as_str,
from_static),Colour(withrgb,rgba,from_rgba_u32,
to_rgba_u32and theWHITE,BLACK,NONEconstants),
Vector2,Vector3,Vector4,ComponentType.vtable—subobject_ptr,vtable_slot,
secondary_call_targethelpers for safe access to secondary
vtables.
samp::ompre-exports the module above.samp::pluginnew functions:enable_tick,
enable_tick_with,omp_core,omp_query_component,
omp_query::<T>(typed wrapper forOmpComponentHandle
implementors).samp::pluginnew types:TickConfig,TickContext,
TickSource.SampPluginnew hooks:on_tick(ctx),
on_omp_ready(gated bynot(feature = "samp-only")),
on_component_free(same gating).samp::logre-export —#[native]-expanded code now uses
samp::log::error!, so user crates no longer need to declare
logas a direct dependency just to satisfy the macro.
Added — initialize_plugin! extensions
- Optional metadata fields:
uid: <u64 expression>,
component_name: "...",component_version: (x, y, z). samp-codegenreads[package.metadata.samp]from the project's
Cargo.toml. Resolution order per field:
macro argument >[package.metadata.samp]> derived value
(CARGO_PKG_NAME, parsedCARGO_PKG_VERSION, FNV-1a 64 of
CARGO_PKG_NAME@CARGO_PKG_VERSIONfor the UID).- When the UID is missing from both sources, the generated value is
persisted back intoCargo.tomlunder[package.metadata.samp]
so subsequent builds reuse the same identifier. - Generates the SA-MP exports (
Load,Unload,Supports,
AmxLoad,AmxUnload,ProcessTick) and the Open Multiplayer
ComponentEntryPointby default. Opt out with thesamp-only
feature.
Added — #[native] extensions
- Accepts associated functions (no
self), in addition to
methods. - Return type detection:
Result/AmxResultis matched against
Ok/Err; any other type implementingAmxCellis used as the
return cell directly (no spuriousOk(...)wrapping). - Accepts
&AmxString(and any other&T) parameters — the macro
materializes the owned value fromargs.next_arg()and injects
&localat the call site. - Validates the
name = "..."literal at proc-macro time:
interior\0bytes now produce a compile error instead of
panicking atCString::newduring server load. - Wraps every invocation in
std::panic::catch_unwind. Panics that
would otherwise cross theextern "C"boundary (process abort on
Rust 1.71+) are caught, logged as
[<NativeName>] panic in native: <payload>, and converted to a
0return. - Argument parsing failures now log
[<NativeName>] failed to parse argument #<i> '<name>' (expected type: <Type>)
— both the positional index and the expected type are included.
Added — features and build infrastructure
- New
samp-onlyfeature on bothsampandsamp-sdk: removes
every Open Multiplayer code path. The plugin still loads on Open
Multiplayer, but in legacy mode (no component API). - New workspace
[profile.release]:lto = "thin",
codegen-units = 1,strip = true. Cargo.lockis now committed (removed from.gitignore).Cargo.tomlper crate exposes
package.metadata.docs.rs.default-target = "i686-pc-windows-msvc"features = ["encoding"]so docs.rs builds with the right
target.
- New build scripts:
scripts/build-linux.sh— produces.so
(i686-unknown-linux-gnu) and.dll
(i686-pc-windows-msvcviacargo-xwin --xwin-arch x86, or
i686-pc-windows-gnuwith--samp-only).scripts/build-windows.sh— produces.dllnatively and.so
through WSL or Docker/cross (autodetected; forceable with
--wsl/--docker).
- New helper scripts used by the benchmark workflow:
scripts/append-bench-history.py,scripts/extract-bench.py,
scripts/render-bench-entry.py. - New GitHub workflows:
docs.yml(publishes the MkDocs site),
release.yml(creates releases onv*tags, attaches a source
tarball with only the essential crates),release-drafter.yml,
labels.yml,bench-release.yml(per-release benchmark history
on thebench-databranch). .github/labels.ymland.github/release-drafter.ymlfor the
workflows above.rust.ymlworkflow: action versions bumped to
actions/checkout@v6,actions/upload-artifact@v7,
actions/cache/restore@v5,actions/cache/save@v5(Node 24
baseline); benchmark job restricted to-p samp-sdkto avoid
Unrecognized option: 'save-baseline'; artefact retention now
capped at 14 days.
Added — tests and benchmarks
- Unit tests grew from 80 (v2.2.0) to 207 (this r...
v2.2.0
Aviso:
A versão 2.1.0 não havia sido declarada anteriormente, então como uma compensação e correção estou trazendo a v2.2.0 com novas adições e melhorias de código. Desde já peço desculpas.
What's Changed
- Update by @NullSablex in #4
Full Changelog: v2.1.0...v2.2.0
v2.1.0
What's Changed
- Fix files optmize by @NullSablex in #1
- Update-cargo by @NullSablex in #2
- Release v2.1.0: segurança, testes, CI e documentação by @NullSablex in #3
New Contributors
- @NullSablex made their first contribution in #1
Full Changelog: https://github.com/NullSablex/rust-samp/commits/v2.1.0
v1.0.1
What's Changed
- Fix files optmize by @NullSablex in #1
Full Changelog: https://github.com/NullSablex/rust-samp/commits/v1.0.1
v1.0.0
Full Changelog: https://github.com/NullSablex/rust-samp/commits/v1.0.0