Prove clean native release artifacts (#106)
Some checks failed
API and SemVer surface / api-surface (push) Failing after 1m28s
Native release artifact audit / audit (push) Failing after 1h6m34s
Native code generation / deterministic (push) Failing after 2m15s
Concurrency and resource soak audit / soak (push) Failing after 6m33s
Documentation / documentation (push) Failing after 2m0s
Imaging and meshing gate / native (push) Failing after 55s
JPEG 2000 feature / linux (push) Successful in 2m46s
performance evidence / audit (push) Failing after 13m45s
Release platform and feature matrix / audit (push) Successful in 1m3s
Native Rust workspace compile / compile (push) Failing after 55s
Skia feature / linux (push) Successful in 31m58s
Dependency and supply-chain audit / audit (push) Failing after 9m34s
Release platform and feature matrix / matrix (false, linux-stable-minimal, x86_64-unknown-linux-gnu, stable) (push) Failing after 10m2s
Release platform and feature matrix / matrix (false, macos-stable-portable, x86_64-apple-darwin, stable) (push) Failing after 2m0s
Release platform and feature matrix / matrix (false, windows-stable-portable, x86_64-pc-windows-gnu, stable) (push) Failing after 1m56s
Release platform and feature matrix / matrix (true, linux-msrv-portable, x86_64-unknown-linux-gnu, 1.96.0) (push) Failing after 6m19s
Release platform and feature matrix / matrix (true, linux-stable-default, x86_64-unknown-linux-gnu, stable) (push) Failing after 6m57s
Release platform and feature matrix / matrix (true, linux-stable-features, x86_64-unknown-linux-gnu, stable) (push) Failing after 7m6s
Release platform and feature matrix / matrix (true, linux-stable-release-surface, x86_64-unknown-linux-gnu, stable) (push) Failing after 8m8s

This commit is contained in:
2026-08-12 04:57:37 +00:00
parent 738fe3933e
commit dceb394378
40 changed files with 20112 additions and 154 deletions

View File

@@ -12,8 +12,8 @@ default = []
jpeg2000 = ["dep:libremetaverse-openjpeg"]
[dependencies]
libremetaverse-types = { path = "../libremetaverse-types" }
libremetaverse-openjpeg = { path = "../libremetaverse-openjpeg", optional = true }
libremetaverse-types = { version = "0.0.1", path = "../libremetaverse-types" }
libremetaverse-openjpeg = { version = "0.0.1", path = "../libremetaverse-openjpeg", optional = true }
[dev-dependencies]
stats_alloc = "0.1.10"

View File

@@ -8,7 +8,7 @@ repository.workspace = true
description = "Native LSL lexer and parser tooling for the MetaCrate LibreMetaverse rewrite"
[dependencies]
libremetaverse-types = { path = "../libremetaverse-types" }
libremetaverse-types = { version = "0.0.1", path = "../libremetaverse-types" }
unicode-general-category = "1.1"
regex = "1"

View File

@@ -1,10 +1,10 @@
[package]
name = "libremetaverse-openjpeg"
version = "0.0.1"
edition = "2024"
rust-version = "1.96"
license = "BSD-3-Clause"
publish = false
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
description = "Safe, bounded OpenJPEG 2.5.4 adapter for MetaCrate"
build = "build.rs"

View File

@@ -6,7 +6,6 @@ rust-version.workspace = true
license.workspace = true
repository.workspace = true
description = "Safe system-libopus adapter for MetaCrate"
publish = false
build = "build.rs"
[build-dependencies]

View File

@@ -8,8 +8,8 @@ repository.workspace = true
description = "Primitive meshing shims for the MetaCrate LibreMetaverse rewrite"
[dependencies]
libremetaverse-imaging = { path = "../libremetaverse-imaging" }
libremetaverse-types = { path = "../libremetaverse-types" }
libremetaverse-imaging = { version = "0.0.1", path = "../libremetaverse-imaging" }
libremetaverse-types = { version = "0.0.1", path = "../libremetaverse-types" }
[dev-dependencies]
stats_alloc = "0.1.10"

View File

@@ -9,12 +9,12 @@ description = "Bounded native MeshFoundry renderer for the MetaCrate LibreMetave
[dependencies]
flate2 = "1.1.2"
libremetaverse = { path = "../libremetaverse" }
libremetaverse-imaging = { path = "../libremetaverse-imaging" }
libremetaverse-prim-mesher = { path = "../libremetaverse-prim-mesher" }
libremetaverse-rendering-simple = { path = "../libremetaverse-rendering-simple" }
libremetaverse-structured-data = { path = "../libremetaverse-structured-data" }
libremetaverse-types = { path = "../libremetaverse-types" }
libremetaverse = { version = "0.0.1", path = "../libremetaverse" }
libremetaverse-imaging = { version = "0.0.1", path = "../libremetaverse-imaging" }
libremetaverse-prim-mesher = { version = "0.0.1", path = "../libremetaverse-prim-mesher" }
libremetaverse-rendering-simple = { version = "0.0.1", path = "../libremetaverse-rendering-simple" }
libremetaverse-structured-data = { version = "0.0.1", path = "../libremetaverse-structured-data" }
libremetaverse-types = { version = "0.0.1", path = "../libremetaverse-types" }
[dev-dependencies]
stats_alloc = "0.1.10"

View File

@@ -8,10 +8,10 @@ repository.workspace = true
description = "Simple renderer shims for the MetaCrate LibreMetaverse rewrite"
[dependencies]
libremetaverse = { path = "../libremetaverse" }
libremetaverse-imaging = { path = "../libremetaverse-imaging" }
libremetaverse-prim-mesher = { path = "../libremetaverse-prim-mesher" }
libremetaverse-types = { path = "../libremetaverse-types" }
libremetaverse = { version = "0.0.1", path = "../libremetaverse" }
libremetaverse-imaging = { version = "0.0.1", path = "../libremetaverse-imaging" }
libremetaverse-prim-mesher = { version = "0.0.1", path = "../libremetaverse-prim-mesher" }
libremetaverse-types = { version = "0.0.1", path = "../libremetaverse-types" }
[lints]
workspace = true

View File

@@ -8,7 +8,7 @@ repository.workspace = true
description = "Bounded native RLV protocol, state, inventory, lock, camera, and permission layer"
[dependencies]
libremetaverse-types = { path = "../libremetaverse-types" }
libremetaverse-types = { version = "0.0.1", path = "../libremetaverse-types" }
[lints]
workspace = true

View File

@@ -9,7 +9,7 @@ description = "OSD and LLSD shims for the MetaCrate LibreMetaverse rewrite"
[dependencies]
base64 = "0.22"
libremetaverse-types = { path = "../libremetaverse-types" }
libremetaverse-types = { version = "0.0.1", path = "../libremetaverse-types" }
serde = "1"
serde_json = "1"

View File

@@ -8,9 +8,9 @@ repository.workspace = true
description = "Utility shims for the MetaCrate LibreMetaverse rewrite"
[dependencies]
libremetaverse = { path = "../libremetaverse" }
libremetaverse-structured-data = { path = "../libremetaverse-structured-data" }
libremetaverse-types = { path = "../libremetaverse-types" }
libremetaverse = { version = "0.0.1", path = "../libremetaverse" }
libremetaverse-structured-data = { version = "0.0.1", path = "../libremetaverse-structured-data" }
libremetaverse-types = { version = "0.0.1", path = "../libremetaverse-types" }
[lints]
workspace = true

View File

@@ -8,9 +8,9 @@ repository.workspace = true
description = "Vivox voice shims for the MetaCrate LibreMetaverse rewrite"
[dependencies]
libremetaverse = { path = "../libremetaverse" }
libremetaverse-structured-data = { path = "../libremetaverse-structured-data" }
libremetaverse-types = { path = "../libremetaverse-types" }
libremetaverse = { version = "0.0.1", path = "../libremetaverse" }
libremetaverse-structured-data = { version = "0.0.1", path = "../libremetaverse-structured-data" }
libremetaverse-types = { version = "0.0.1", path = "../libremetaverse-types" }
roxmltree = "0.21.1"
tokio = { version = "1.47.1", features = ["io-util", "net", "time"] }

View File

@@ -9,10 +9,10 @@ description = "WebRTC voice shims for the MetaCrate LibreMetaverse rewrite"
[dependencies]
hound = "3.5"
libremetaverse = { path = "../libremetaverse" }
libremetaverse-opus = { path = "../libremetaverse-opus" }
libremetaverse-structured-data = { path = "../libremetaverse-structured-data" }
libremetaverse-types = { path = "../libremetaverse-types" }
libremetaverse = { version = "0.0.1", path = "../libremetaverse" }
libremetaverse-opus = { version = "0.0.1", path = "../libremetaverse-opus" }
libremetaverse-structured-data = { version = "0.0.1", path = "../libremetaverse-structured-data" }
libremetaverse-types = { version = "0.0.1", path = "../libremetaverse-types" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
str0m = { version = "0.22", default-features = false, features = ["rust-crypto"] }

View File

@@ -19,9 +19,9 @@ bcdec_rs = { version = "0.2.0", optional = true }
flate2 = "1.1.2"
futures-channel = "0.3.31"
futures-util = "0.3.31"
libremetaverse-imaging = { path = "../libremetaverse-imaging" }
libremetaverse-structured-data = { path = "../libremetaverse-structured-data" }
libremetaverse-types = { path = "../libremetaverse-types" }
libremetaverse-imaging = { version = "0.0.1", path = "../libremetaverse-imaging" }
libremetaverse-structured-data = { version = "0.0.1", path = "../libremetaverse-structured-data" }
libremetaverse-types = { version = "0.0.1", path = "../libremetaverse-types" }
mac_address2 = "2.0.2"
os_info = { version = "3.15.0", default-features = false }
reqwest = { version = "0.13.4", default-features = false, features = ["rustls", "stream"] }

View File

@@ -0,0 +1,267 @@
# Rust API and C# migration guide
`MetaCrate` is a native Rust implementation of the API and behavior cataloged
from `LibreMetaverse`. The compatibility reference is the
[pinned C# source](https://github.com/cinderblocks/libremetaverse/tree/2aa70bb68513b39795da5d13c88f31b86e85a3ba);
it is never loaded, invoked, or shipped. Exact type/member mappings live in
[`RUST-TYPES.tsv`](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/api/RUST-TYPES.tsv) and
[`RUST-MAPPING.tsv`](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/api/RUST-MAPPING.tsv). Search those files by C#
documentation ID when a familiar member has a non-obvious Rust name.
## Choosing crates and features
Most applications start with `libremetaverse` and add narrower crates only when
they use those APIs directly.
| Crate | Choose it for | Features or system boundary |
| --- | --- | --- |
| `libremetaverse` | Grid client, login, network, agents, inventory, assets, appearance, world, and social APIs | Default pure-Rust BC6H/BC7; optional `jpeg2000` and `vorbis` |
| `libremetaverse-types` | UUIDs, vectors, matrices, colors, cancellation, compatibility collections, and boundary types | Pure Rust |
| `libremetaverse-structured-data` | LLSD/OSD XML, JSON, binary, and notation | Pure Rust |
| `libremetaverse-imaging` | Managed images, TGA/DDS, codec traits | Optional `jpeg2000` |
| `libremetaverse-imaging-skia` | Common raster formats through Skia | Optional `skia`; native Skia build/cache |
| `libremetaverse-prim-mesher` | Legacy prim and sculpt geometry | Pure Rust |
| `libremetaverse-rendering-simple` | Deterministic reference geometry | Pure Rust |
| `libremetaverse-rendering-mesh-foundry` | Prim, terrain, sculpt, and mesh-asset rendering | Pure Rust |
| `libremetaverse-lsl-tools` | LSL lexing, parsing, diagnostics, and generation | Pure Rust |
| `libremetaverse-rlv` | RLV commands, restrictions, locks, camera, and inventory policy | Pure Rust |
| `libremetaverse-utilities` | Compatible utility helpers | Pure Rust |
| `libremetaverse-voice-vivox` | Vivox XML control protocol | External Vivox service is explicit and never spawned |
| `libremetaverse-voice-webrtc` | Native ICE/DTLS/SRTP/SCTP and Opus voice | System libopus; optional `real-audio` uses CPAL |
| `libremetaverse-openjpeg` | Audited `OpenJPEG` adapter | System `OpenJPEG` 2.5.4 or newer |
| `libremetaverse-opus` | Audited Opus encoder/decoder adapter | System libopus |
Features are additive. Keep defaults unless you need a codec, and enable one
native adapter at a time while diagnosing installation problems. The
[release CI matrix](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/docs/release-ci-matrix.md) records every validated combination.
## Naming and overload migration
C# `PascalCase` types remain recognizable while methods and properties use
Rust `snake_case`. A property getter becomes `name()` and its setter becomes
`set_name(value)`. Events become `subscribe_*` methods returning an owned
subscription. Rust has no overloads, so the simplest form keeps the base name
and additional forms receive parameter-derived suffixes. The mapping ledger is
authoritative; do not guess a long suffix.
```rust
use libremetaverse::InventoryItem;
use libremetaverse_types::UUID;
let id = UUID::new_with_u_int64(42)?;
let mut item = InventoryItem::new_with_uuid(id)?;
item.base.set_name("Migrated item".into());
assert_eq!(item.base.uuid(), id);
assert_eq!(item.base.name(), "Migrated item");
# Ok::<(), libremetaverse::Error>(())
```
C# `null` usually maps to `Option<T>`, `ref`/`out` may become a return value or
an explicit mutable reference, and interface objects become `dyn Trait` behind
`Arc` or `Box` according to the ledger's `ownership` column.
## Ownership and disposal
Managers cloned from a `GridClient` share native state. `GridClient` owns its
cancellation root and cached services; event guards own registrations; session
objects own tasks and sockets. Explicit shutdown is recommended because it can
report errors, while `Drop` remains the final idempotent safety net.
```rust
use libremetaverse::{ClientLifecycleState, GridClient};
let client = GridClient::new()?;
assert_eq!(client.lifecycle_state(), ClientLifecycleState::Active);
client.dispose_with_method()?;
client.dispose_with_method()?; // idempotent
assert_eq!(client.lifecycle_state(), ClientLifecycleState::Disposed);
# Ok::<(), libremetaverse::Error>(())
```
The runnable version is
[`offline_client.rs`](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/crates/libremetaverse/examples/offline_client.rs).
Never hold a manager lock while calling user code or awaiting I/O. Services
registered through `GridClient::builder()` must join every owned task before
their `shutdown` method returns.
## Async work and cancellation
Async APIs borrow no hidden runtime. Call them from the application executor and
pass `CancellationToken` explicitly where the C# API accepted one. Cloning a
token is cheap and observes the same cancellation source. Cancellation is a
typed `Error::Cancelled`, not a successful empty response.
```rust
use libremetaverse_types::compat::CancellationTokenSource;
# #[tokio::main(flavor = "current_thread")]
# async fn main() -> Result<(), libremetaverse::Error> {
let source = CancellationTokenSource::new();
let token = source.token();
source.cancel();
token.cancelled().await;
assert!(matches!(
token.throw_if_cancellation_requested(),
Err(libremetaverse::Error::Cancelled)
));
# Ok(())
# }
```
The same code is available as
[`cancellation.rs`](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/crates/libremetaverse/examples/cancellation.rs).
Timeouts belong at the caller or documented operation boundary; cancellation
must still drain and join the underlying resource owner.
## Errors
Fallible compatibility APIs return the shared `libremetaverse::Error`; native
composition APIs may expose a narrower error such as `ClientCoreError` or
`WebRtcError`. Match variants for control flow and use display text only for
operators. Errors and `Debug` output redact credentials, capability URLs, and
session secrets.
```rust
use libremetaverse::http::DownloadRequest;
use libremetaverse_types::compat::Uri;
let result = DownloadRequest::new(Uri("file:///not-http".into()), None, None);
assert!(matches!(result, Err(libremetaverse::Error::Argument)));
```
Do not translate C# exception swallowing into `unwrap_or_default()`. Preserve
the mapped error contract and handle cancellation separately from protocol,
I/O, authentication, and validation failures.
## Events and subscriptions
An `EventHandler<T>` is an `Arc` callback. Keep the returned `Subscription` for
exactly as long as notifications are wanted; dropping it unregisters the
callback. Dispatch clones the handler list and releases internal locks before
calling user code. Manager event dispatch isolates subscriber panics so one
consumer cannot stop later consumers.
```rust
use libremetaverse::{GridClient, Inventory, InventoryFolder};
use libremetaverse_types::UUID;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
let client = Arc::new(GridClient::new()?);
let inventory = Inventory::new_with_grid_client_uuid(Arc::clone(&client), UUID::zero())?;
let calls = Arc::new(AtomicUsize::new(0));
let observed = Arc::clone(&calls);
let subscription = inventory.subscribe_inventory_object_added(Arc::new(move |_| {
observed.fetch_add(1, Ordering::AcqRel);
}));
let folder = InventoryFolder::new(UUID::new_with_u_int64(7)?)?;
inventory.update_node_for(&folder)?;
assert_eq!(calls.load(Ordering::Acquire), 1);
drop(subscription);
client.dispose_with_method()?;
# Ok::<(), libremetaverse::Error>(())
```
## Threading and callbacks
Public shared managers use `Arc`, atomics, mutexes, channels, and cancellation
tokens rather than a CLR synchronization context. A type being cloneable does
not make a callback re-entrant: keep handlers short, move expensive work to a
bounded queue, and never block an async runtime thread waiting for itself.
Download, inventory, client, and voice diagnostics expose owned task/queue
counts for shutdown checks. The [concurrency audit](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/docs/concurrency-hardening.md)
defines the exact baseline and soak thresholds.
## Security boundaries
The released graph contains no CLR host, .NET assembly loader, subprocess RPC,
or foreign `LibreMetaverse` bridge. HTTP uses rustls. Untrusted LLSD, archive,
image, packet, event, and signaling inputs have explicit size/depth limits.
Secrets belong in operation arguments or environment variables, never in
diagnostics, evidence, filenames, command lines, or committed fixtures.
Offline/fake modes are real deterministic executions, not skipped live tests.
Live login and every mutating smoke action require their own opt-in and literal
confirmation. See the [live-grid boundary](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/docs/live-grid-smoke.md).
## Native prerequisites
The default client build needs no image-codec system library. Optional native
features require:
- `OpenJPEG` 2.5.4 or newer for `jpeg2000`;
- the rust-skia prerequisites/cache for `skia`;
- system libopus for WebRTC voice and the `libremetaverse-opus` adapter;
- ALSA development headers on Linux, `CoreAudio` on macOS, or WASAPI on Windows
when `real-audio` enables CPAL;
- the Vorbis encoder build prerequisites for `vorbis`.
Discovery uses `pkg-config` on Unix/macOS and `vcpkg` on Windows MSVC where
applicable. The adapters remain cross-platform; Gitea workflows intentionally
run only on `ubuntu-latest`.
## Live `OpenSim` setup
Use a dedicated `OpenSim` account and put credentials in the workspace `.env` or
the process environment. The login URL is used directly; no Second Life host is
substituted.
```text
GRID_USER=First Last
GRID_PASSWORD=...
GRID_LOGIN_URL=https://your-opensim.example/login
```
Start with the credential-safe audit and fake smoke, then opt into live login:
```sh
cargo run -p libremetaverse-programs --bin live-grid-smoke -- --audit-only
cargo run -p libremetaverse-programs --bin live-grid-smoke -- --fake
cargo run -p libremetaverse-programs --bin live-grid-smoke -- \
--allow-live-login --confirm-live-login LOGIN
```
Chat, movement, and reversible inventory each require additional confirmations
documented in the [`OpenSim` live-grid guide](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/docs/live-grid-smoke.md). A test that
needs live credentials must fail clearly when they are absent; it must not
silently skip.
## Programs and operational tools
All pinned upstream programs have native Rust targets and deterministic offline
tests. Their arguments, exit statuses, fake/live boundaries, and focused test
commands are linked from the [program operations manual](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/programs/README.md):
- [`osd-inspector`](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/programs/README.md#osdinspector)
- [`simple-bot`](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/programs/README.md#simplebot)
- [`packet-dump`](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/programs/README.md#packetdump)
- [`prim-inspector`](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/programs/README.md#priminspector)
- [`inventory-explorer`](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/programs/README.md#inventoryexplorer)
- [`irc-gateway`](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/programs/README.md#ircgateway)
- [`test-client`](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/programs/README.md#testclient)
- [`vivox-test`](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/programs/README.md#vivoxtest)
- [`webrtc-test`](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/programs/README.md#webrtctest)
For services, run the client under an external supervisor, propagate shutdown
cancellation, bound queues/files, collect sanitized evidence, and call logout
before disposal. The tool never invents persistence or retry policy on behalf of
the application.
## Documentation validation
The complete documentation gate is:
```sh
cargo run --locked -p metacrate-ci-matrix -- documentation-audit \
--evidence /tmp/metacrate-documentation.json
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps --locked -j 1
cargo test --locked -p libremetaverse --doc -j 1
cargo test --locked -p libremetaverse --examples -j 1
cargo run --locked -p libremetaverse --example offline_client
cargo run --locked -p libremetaverse --example cancellation
```
The generated [coverage report](https://git.rfc1437.de/hugo/MetaCrate/src/branch/main/api/DOCUMENTATION-COVERAGE.md) proves that
every mapped member/type retains its exact C# concept ID and mapping context,
all public crates have root docs, all local Markdown links resolve, all programs
are linked, and every Rust fence in this guide is compiled as a doctest.

File diff suppressed because it is too large Load Diff

View File

@@ -113,7 +113,7 @@ impl LindenAvatarDefinition {
owned_xml = read_bounded(&file_name)?;
owned_xml.as_str()
} else {
include_str!("../../../codegen/inputs/avatar_lad.xml")
include_str!("../assets/avatar_lad.xml")
};
let document = Document::parse(xml).map_err(|_| Error::Argument)?;
let mut attachment_points = Vec::new();

View File

@@ -4,7 +4,7 @@
//! world, and social APIs. The appended guide covers crate selection, C# name
//! migration, ownership, cancellation, events, security, and `OpenSim` setup.
#![doc = include_str!("../../../docs/rust-api-guide.md")]
#![doc = include_str!("../README.md")]
extern crate self as libremetaverse;