v3.2.0
New features
-
VM debugging primitives on
Amx— safe accessors that previously had to
be hand-written by tooling poking the#[repr(C, packed)]AMXstruct:
register reads (cip,frame,stack,heap,stp), bounds-checked
data-segment cell access (read_cell/write_cell, mirroringamx_GetAddr
and usable inside a debug hook where no native context exists), and debug
hook management (install_debug_hook/remove_debug_hook, the equivalent of
amx_SetDebugHook). Always available, no feature gate. -
samp::debug— AMX_DBG debug-info parser (featuredebug) — pure-logic
decoder for the debug blockpawncc -d2/-d3appends to the.amx. Maps a
code address ↔ source line ↔ symbol ↔ function (AmxDbg::from_amx/parse,
lookup_line,lookup_file,lookup_function,line_to_address,
symbols_in_scope,tag_name), handling the 16-bit line-count overflow of
large gamemodes and corrupted-count sanity ceilings. No extra dependencies;
opt-in via thedebugfeature.DbgSymbol::effective_address(frm)and
DbgSymbol::is_array()remove the global-vs-frame address boilerplate when
pairing the parser withAmx::read_cell/write_cell. -
External sinks (
samp::logger::Sinktrait +LoggerConfig::add_sink) —
extension point for forwarding accepted log records to a destination
chosen by the plugin author (Sentry, an OTLP collector, an in-house
HTTP endpoint, anything). No telemetry is built into the SDK. No
dependency onsentry/opentelemetryis added;rust-samp
ships exactly the same dependency graph as before. The trait is an
opt-in surface only — implementing it is the plugin author's call,
and instances become active only through an explicit
LoggerConfig::add_sink(Box::new(...))in the plugin's own source.
The SDK contains zeroadd_sinkinvocations of its own; server
operators auditing what arust-sampplugin can export only need
to grep its source foradd_sink(. Zero hits means zero external
traffic from the logger. There is no hidden flag, no environment
override, and no default destination — this is not Microsoft-style
always-on telemetry, it is a hook for plugin authors who already
run their own observability stack to integrate with it on their own
terms. -
samp::version()— free function returning theCARGO_PKG_VERSION
of therust-samp(samp) crate. Pair it with a Pawn-side native
(e.g.MyPlugin_GetSdkVersion()) to surface the active SDK build in
bug reports and diagnostic dashboards. -
samp::logger::flush()— public free function that flushes the
active log file directly through the liveLoggerImpl. Going through
log::logger().flush()did not guarantee a sync of the SDK's own
file handle; callingsamp::logger::flush()does. Safe no-op when
the logger has not been installed — meant for panic hooks and
custom shutdown paths. -
LoggerConfig::from_env()— applies runtime overrides from
environment variables, so server operators can flip the log level,
redirect the directory, change the rotation threshold etc. without
recompiling the plugin. The prefix is derived from the plugin's
crate name uppercased with non-alphanumeric characters replaced by
_(streamer-rs→STREAMER_RS_LOG_*). Recognised keys:
LEVEL,DIR,FILE,ROTATION_MB,ROTATION_KEEP,
NO_ROTATION,NO_BANNER,SERVER, andCOMPRESS(the last only
effective when thecompressionfeature is enabled). Missing vars
leave the existing value untouched; invalid values are reported to
the server console and the previous value is kept. Pairs with
Runtime::try_get()(also new) so the parser can warn gracefully
even when called before the runtime is initialised (e.g. from a
unit test). -
LoggerConfig::compress_archives(bool)— opt-in gzip of rotated
archives. When enabled, every rotation produces
{filename}.{N}.gzinstead of{filename}.{N}and removes the
uncompressed file. Works with both rotation strategies (append-style
androtation_keep(N)shift-style). Gated by the newcompression
Cargo feature, which pulls inflate2with the pure-Rust backend —
not enabled by default, so plugins that do not need it pay no extra
dependency cost. The next-archive scan also recognizes.gz
variants so an index is never reused across restarts. -
Amx::call_native()— invoke a native registered by another
plugin in the same AMX, straight from Rust. Resolves the host
function pointer throughamx_FindNative+ the natives table in the
AMX_HEADER, builds theparamsblock in the AMX convention
([argc * sizeof(cell), arg0, ...]) and surfaces VM-side errors back
viaamx.error. Unblocks integration with the entire existing C++
plugin ecosystem (Streamer, MySQL, sscanf, …) without dropping down
tosamp_sdk::raw. Originally surfaced by
@Day-OS (Discord@daytheipc), who
foundrust-sampon crates.io while trying to drive the Streamer
plugin from Rust for an in-game PNG / video / YouTube-live 3D panel
and hit the gap that this API closes. May or may not have been
exactly what she needed — but it should help.
Examples
- New
examples/sink-demo/— complete, working Sentry
integration for the newSinktrait. Uses the realsentry
crate (sentry = "0.43"withreqwest+rustls+contexts,
default-features = false), withsentry::initand
sentry::capture_eventwired up end-to-end — everylog!call
becomes a real Sentry event. DSN is read from the env var
SINK_DEMO_SENTRY_DSNat plugin load — never hardcoded. Source
code stays clean, the DSN stays in the operator's environment
(systemdEnvironment=, Docker secret, vault sidecar,.env
outside the repo, …). Implements the full backpressure pattern
(mpsc::sync_channelbetween the logger lock and Sentry +
dedicated background drainer thread that owns the
ClientInitGuard, so itsDropflushes pending events at plugin
unload). When the env var is missing the example falls back to a
fake local DSN (http://fake@127.0.0.1:9999/1) — the Sentry
client still initializes but its HTTP transport refuses fast, so
no event ever reaches a real Sentry server. Going to production
is oneexportstatement. When a real DSN is configured, the
plugin also emits a startup smoke test (oneinfo+ one
warning+ oneerror) onon_loadso the operator immediately
sees the wiring working on the Sentry dashboard. The heavy
sentrydep (pinned to 0.48.3) is paid by this example crate,
not by the SDK. Pawn natives:SinkDemo_GetExportedCount,
SinkDemo_GetDroppedCountfor pipeline observability;
SinkDemo_EmitInfo,SinkDemo_EmitWarn,SinkDemo_EmitError
for firing test events at each severity from the gamemode.
Build
- New Cargo feature
compressionon therust-sampcrate. Opt-in;
pulls inflate2 = "1"with the pure-Rust backend
(default-features = false,features = ["rust_backend"]) so plugins
that do not enable it remain dependency-free on this axis. timebumped to>= 0.3.47(also pulls intime-core 0.1.8
andtime-macros 0.2.27) — this is the floor that drove the MSRV bump;
later refreshed to 0.3.51 by Dependabot (see Dependencies, #16).- MSRV bumped to Rust 1.88 (was 1.87) to satisfy those versions.
Declared via[workspace.package].rust-version = "1.88".
Security & governance
- OpenSSF Scorecard — new
.github/workflows/scorecard.ymlthat runs
the OpenSSF Scorecard analysis, uploads the SARIF to code-scanning and
publishes the result. Scorecard badge added to the README. - All GitHub Actions pinned by commit SHA — every
uses:across all
seven workflows is pinned to a full commit SHA (with a# vXcomment),
satisfying the Scorecard Pinned-Dependencies check. - Least-privilege token permissions — every workflow declares a
top-level minimalpermissions: contents: read, with jobs escalating
explicitly only where needed (Scorecard Token-Permissions). - No script injection from untrusted PR fields —
rust.ymlnow passes
github.event.pull_request.*values (e.g.head.ref) throughenv
instead of interpolating them intorun:scripts (Scorecard
Dangerous-Workflow). docs/requirements.txtpinned by hash — the MkDocs Material build
dependencies are now a fully hashed lockfile (pip-compile --generate-hashesfrom the newdocs/requirements.in), installed with
pip install --require-hashesin the docs workflow..github/dependabot.yml— weekly version updates for the
github-actionsandcargoecosystems, keeping the pinned SHAs and
crate dependencies fresh (Scorecard Dependency-Update-Tool).- CI tweaks — the Scorecard workflow gained
workflow_dispatchfor
on-demand re-scans; the Rust workflow ignores docs-only changes via
paths-ignore(**.md,docs/**,mkdocs.yml,LICENSE); and the
benchmark job now runs only when benchmark-relevant code actually changed
(achangespath-filter job gates it), never fordependabot[bot]
(dependency bumps don't need a bench run, and Dependabot's read-only
token cannot post the PR comment). - Release Drafter removed —
.github/release-drafter.ymland
.github/workflows/release-drafter.ymldropped. Releases are cut
directly rather than drafted, so the workflow was dead weight. SECURITY.md— security policy and private vulnerability reporting
via GitHub Security Advisory.CODE_OF_CONDUCT.md— Contributor Covenant 2.1.CONTRIBUTING.md— build/test/lint workflow for the i686 targets,
project structure and code rules.
Documentation
- Single source of truth for crate versions — the per-crate version
columns were removed from theREADME.mdanddocs/index.mdworkspace
tables (they duplicatedCargo.tomland had already drifted out of date).
Version-specific git tags in install snippets across the docs were replaced
with atag = "vX.Y.Z"placeholder, and the dependency examples already use
the major-onlyversion = "3". Bumping a crate now means editing its
Cargo.tomland adding a CHANGELOG entry — no docs need touching.
Dependencies
Automated bumps opened and merged via @dependabot
after the new dependabot.yml went live:
- Bump
actions/cache/savefrom 5.1.0 to 6.1.0 (#8) - Bump
actions/cache/restorefrom 5.1.0 to 6.1.0 (#9) - Bump
actions/checkoutfrom 4.2.2 to 7.0.0 (#10) - Bump
marocchino/sticky-pull-request-commentfrom 2.9.4 to 3.0.4 (#12) - Bump
logfrom 0.4.29 to 0.4.33 (#11) - Bump
bitflagsfrom 2.11.0 to 2.13.0 (#13) - Bump
quotefrom 1.0.44 to 1.0.46 (#14) - Bump
synfrom 2.0.116 to 2.0.118 (#15) - Bump
timefrom 0.3.47 to 0.3.51 (#16)
The cargo bumps are lock-file only (Cargo.lock); the version
requirements in the manifests are unchanged.
Crate versions
rust-samp(libsamp): 3.1.0 → 3.2.0rust-samp-sdk(libsamp_sdk): 3.0.0 → 3.1.0 (new VM debugging
primitives onAmxand thesamp::debugparser are additive public API)rust-samp-codegen(libsamp_codegen): 1.3.0 — unchanged
CHANGELOG correction (v3.1.0)
The v3.1.0 entry below described the CNAME removal as a switch to
the default GitHub Pages URL. That was wrong: the file was simply
unnecessary and the documentation URL is unchanged. Recorded here;
the v3.1.0 section below is left as-published.
New Contributors
- @dependabot[bot] made their first contribution in #13
Full Changelog: v3.1.0...v3.2.0