Implement native RLV callback service integration (#80)
Some checks failed
Native code generation / deterministic (push) Failing after 8m21s
Imaging and meshing gate / native (push) Failing after 4m52s
Native Rust workspace compile / compile (push) Failing after 7m14s

This commit is contained in:
2026-08-11 01:00:48 +00:00
parent 362fa62059
commit 2f1c15c081
18 changed files with 2687 additions and 624 deletions

View File

@@ -3,11 +3,10 @@
`libremetaverse-rlv` implements the protocol and pure state boundary for the
Restrained Love Viewer support in the pinned LibreMetaverse snapshot. The
side-effect-free parser converts one bounded chat message into typed commands.
Independent, thread-safe managers then store restrictions and evaluate
inventory, folder locks, camera limits, blacklists, and permissions without
performing network I/O. Command execution, viewer callbacks, replies, and
`GridClient` integration remain separate milestone work that consumes this
layer.
Independent, thread-safe managers store restrictions and evaluate inventory,
folder locks, camera limits, blacklists, and permissions without performing
network I/O. The callback-driven service then executes those typed decisions
through explicit host adapters.
## Message contract
@@ -83,8 +82,39 @@ target rules, permissive exception precedence, IM/chat/channel behavior,
teleport limits, edit/touch/hover decisions, shared and unshared wear, and
folder attachment locks. Camera aggregation applies the reference min/max,
clamping, averaging, alias, texture, and lock rules. The case-insensitive
blacklist returns a sorted snapshot. These providers are deterministic,
thread-safe, and contain no network or callback dependencies.
blacklist returns a sorted snapshot. These providers are deterministic and
thread-safe with no network access. Object-source cleanup snapshots the
affected restrictions and notification listeners, then invokes the injected
action callback after releasing every manager lock.
## Callback and service contract
`RlvService` owns bounded chat and instant-message dispatch, typed action and
get-request routing, restriction lifecycle notifications, and inventory,
attachment, outfit, public-message, sit, and unsit reports. A chat message is
rejected before dispatch when it exceeds the parser's 64 KiB or 128-command
limits. Multi-command processing retains source order and continues after a
well-formed command returns `false`, matching the reference aggregate result.
Blacklist replies, query channels, version strings, restriction messages, and
case-sensitive general notification filters preserve the pinned behavior.
Hosts implement the `IRlvQueryCallbacks` and `IRlvActionCallbacks` `Send + Sync`
traits. Query adapters expose snapshots from the completed inventory and agent
services; action adapters perform attachment, outfit, camera, group, movement,
teleport, chat, and reply operations through those services instead of
reaching into their state. The safe default adapters reproduce the reference
query values and no-op actions while still observing cancellation. Every host
future is awaited only after service and restriction-manager locks have been
released, so recording callbacks may synchronously inspect service state.
The command processor refreshes `InventoryMap` through the query boundary and
publishes the same snapshot to `RlvPermissionsService` before folder or item
decisions. Attachment requests are value objects with a stable cross-platform
hash. Folder walks preserve deterministic order, skip private and hidden
content where required, deduplicate linked items, honor `nostrip`, body-part,
link-exception, and recursive-lock rules, and never manufacture an action for
an invalid target. Cancellation is checked before processing, reports,
inventory refreshes, each multi-command element, and every callback.
## Reproducible verification
@@ -94,11 +124,16 @@ Run the issue-owned checks with one build job:
CARGO_BUILD_JOBS=1 cargo test -p libremetaverse-rlv --locked
CARGO_BUILD_JOBS=1 cargo test -p libremetaverse-compat-tests --test rlv_common_semantics --locked
CARGO_BUILD_JOBS=1 cargo test -p libremetaverse-compat-tests --test rlv_inventory_map_semantics --locked
CARGO_BUILD_JOBS=1 cargo test -p libremetaverse-compat-tests --test rlv_query_basics_semantics --locked
CARGO_BUILD_JOBS=1 cargo test -p libremetaverse-compat-tests --test rlv_attach_commands_semantics --locked
CARGO_BUILD_JOBS=1 cargo test -p libremetaverse-compat-tests --test rlv_notify_semantics --locked
CARGO_BUILD_JOBS=1 cargo clippy -p libremetaverse-rlv --all-targets --locked -- -D warnings
RUSTDOCFLAGS='-D warnings' CARGO_BUILD_JOBS=1 cargo doc -p libremetaverse-rlv --no-deps --locked
python3 tools/check_milestone_10_issue_78.py
python3 tools/check_milestone_10_issue_79.py
python3 tools/check_milestone_10_issue_80.py
```
These commands are cross-platform. The Gitea workflow runs the audit and
workspace compile on `ubuntu-latest`.
The complete issue-owned compatibility slice contains 616 translated RLV
cases across 24 test binaries. These commands are cross-platform. The Gitea
workflow runs the audit and workspace compile on `ubuntu-latest`.

View File

@@ -6,47 +6,13 @@
#![allow(non_snake_case)]
/// C# type: `T:LibreMetaverse.RLV.AttachmentRequest`.
pub struct AttachmentRequest;
impl AttachmentRequest {
/// C# member: `M:LibreMetaverse.RLV.AttachmentRequest.#ctor(System.Guid,LibreMetaverse.RLV.RlvAttachmentPoint,System.Boolean)`.
pub fn new(
item_id: libremetaverse_types::compat::Guid,
attachment_point: libremetaverse_rlv::RlvAttachmentPoint,
replace_existing_attachments: bool,
) -> Result<Self, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.AttachmentRequest.#ctor(System.Guid,LibreMetaverse.RLV.RlvAttachmentPoint,System.Boolean)",
)
}
/// C# member: `M:LibreMetaverse.RLV.AttachmentRequest.Equals(System.Object)`.
pub fn equals(&self, obj: Option<libremetaverse_types::compat::Object>) -> bool {
libremetaverse_types::unimplemented_api!(
"M:LibreMetaverse.RLV.AttachmentRequest.Equals(System.Object)"
)
}
/// C# member: `M:LibreMetaverse.RLV.AttachmentRequest.GetHashCode`.
pub fn get_hash_code(&self) -> i32 {
libremetaverse_types::unimplemented_api!(
"M:LibreMetaverse.RLV.AttachmentRequest.GetHashCode"
)
}
/// C# member: `P:LibreMetaverse.RLV.AttachmentRequest.AttachmentPoint`.
pub fn attachment_point(&self) -> libremetaverse_rlv::RlvAttachmentPoint {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.RLV.AttachmentRequest.AttachmentPoint"
)
}
/// C# member: `P:LibreMetaverse.RLV.AttachmentRequest.ItemId`.
pub fn item_id(&self) -> libremetaverse_types::compat::Guid {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.RLV.AttachmentRequest.ItemId")
}
/// C# member: `P:LibreMetaverse.RLV.AttachmentRequest.ReplaceExistingAttachments`.
pub fn replace_existing_attachments(&self) -> bool {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.RLV.AttachmentRequest.ReplaceExistingAttachments"
)
}
}
/// C# member: `M:LibreMetaverse.RLV.AttachmentRequest.#ctor(System.Guid,LibreMetaverse.RLV.RlvAttachmentPoint,System.Boolean)`.
/// C# member: `M:LibreMetaverse.RLV.AttachmentRequest.Equals(System.Object)`.
/// C# member: `M:LibreMetaverse.RLV.AttachmentRequest.GetHashCode`.
/// C# member: `P:LibreMetaverse.RLV.AttachmentRequest.AttachmentPoint`.
/// C# member: `P:LibreMetaverse.RLV.AttachmentRequest.ItemId`.
/// C# member: `P:LibreMetaverse.RLV.AttachmentRequest.ReplaceExistingAttachments`.
pub use crate::service::AttachmentRequest;
/// C# type: `T:LibreMetaverse.RLV.CameraRestrictions`.
pub use crate::state::CameraRestrictions;
@@ -157,7 +123,7 @@ impl CameraSettings {
}
/// C# type: `T:LibreMetaverse.RLV.IRlvActionCallbacks`.
pub trait IRlvActionCallbacks {
pub trait IRlvActionCallbacks: Send + Sync {
/// C# member: `M:LibreMetaverse.RLV.IRlvActionCallbacks.AdjustHeightAsync(System.Single,System.Single,System.Single,System.Threading.CancellationToken)`.
fn adjust_height(
&self,
@@ -379,7 +345,7 @@ pub trait IRlvActionCallbacks {
}
/// C# type: `T:LibreMetaverse.RLV.IRlvQueryCallbacks`.
pub trait IRlvQueryCallbacks {
pub trait IRlvQueryCallbacks: Send + Sync {
/// C# member: `M:LibreMetaverse.RLV.IRlvQueryCallbacks.IsSittingAsync(System.Threading.CancellationToken)`.
fn is_sitting(
&self,
@@ -654,186 +620,24 @@ impl LockedFolderPublic {
}
/// C# type: `T:LibreMetaverse.RLV.RlvActionCallbacksDefault`.
pub struct RlvActionCallbacksDefault;
impl RlvActionCallbacksDefault {
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.#ctor`.
pub fn new() -> Result<Self, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvActionCallbacksDefault.#ctor",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.AdjustHeightAsync(System.Single,System.Single,System.Single,System.Threading.CancellationToken)`.
pub async fn adjust_height(
&self,
distance: f32,
factor: f32,
delta: f32,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvActionCallbacksDefault.AdjustHeightAsync(System.Single,System.Single,System.Single,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.AttachAsync(System.Collections.Generic.IReadOnlyList{LibreMetaverse.RLV.AttachmentRequest},System.Threading.CancellationToken)`.
pub async fn attach(
&self,
items_to_attach: Vec<libremetaverse_rlv::AttachmentRequest>,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvActionCallbacksDefault.AttachAsync(System.Collections.Generic.IReadOnlyList{LibreMetaverse.RLV.AttachmentRequest},System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.DetachAsync(System.Collections.Generic.IReadOnlyList{System.Guid},System.Threading.CancellationToken)`.
pub async fn detach(
&self,
item_ids: Vec<libremetaverse_types::compat::Guid>,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvActionCallbacksDefault.DetachAsync(System.Collections.Generic.IReadOnlyList{System.Guid},System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.RemOutfitAsync(System.Collections.Generic.IReadOnlyList{System.Guid},System.Threading.CancellationToken)`.
pub async fn rem_outfit(
&self,
item_ids: Vec<libremetaverse_types::compat::Guid>,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvActionCallbacksDefault.RemOutfitAsync(System.Collections.Generic.IReadOnlyList{System.Guid},System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SendInstantMessageAsync(System.Guid,System.String,System.Threading.CancellationToken)`.
pub async fn send_instant_message(
&self,
target_user: libremetaverse_types::compat::Guid,
message: String,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SendInstantMessageAsync(System.Guid,System.String,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SendReplyAsync(System.Int32,System.String,System.Threading.CancellationToken)`.
pub async fn send_reply(
&self,
channel: i32,
message: String,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SendReplyAsync(System.Int32,System.String,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SetCamFOVAsync(System.Single,System.Threading.CancellationToken)`.
pub async fn set_cam_fov(
&self,
fov_in_radians: f32,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SetCamFOVAsync(System.Single,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SetDebugAsync(System.String,System.String,System.Threading.CancellationToken)`.
pub async fn set_debug(
&self,
setting_name: String,
setting_value: String,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SetDebugAsync(System.String,System.String,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SetEnvAsync(System.String,System.String,System.Threading.CancellationToken)`.
pub async fn set_env(
&self,
setting_name: String,
setting_value: String,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SetEnvAsync(System.String,System.String,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SetGroupAsync(System.Guid,System.String,System.Threading.CancellationToken)`.
pub async fn set_group_with_guid_string_cancellation_token(
&self,
group_id: libremetaverse_types::compat::Guid,
role_name: Option<String>,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SetGroupAsync(System.Guid,System.String,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SetGroupAsync(System.String,System.String,System.Threading.CancellationToken)`.
pub async fn set_group_with_string_string_cancellation_token(
&self,
group_name: String,
role_name: Option<String>,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SetGroupAsync(System.String,System.String,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SetRotAsync(System.Single,System.Threading.CancellationToken)`.
pub async fn set_rot(
&self,
angle_in_radians: f32,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SetRotAsync(System.Single,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SitAsync(System.Guid,System.Threading.CancellationToken)`.
pub async fn sit(
&self,
target: libremetaverse_types::compat::Guid,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SitAsync(System.Guid,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SitGroundAsync(System.Threading.CancellationToken)`.
pub async fn sit_ground(
&self,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SitGroundAsync(System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.TpToAsync(System.Single,System.Single,System.Single,System.String,System.Nullable{System.Single},System.Threading.CancellationToken)`.
pub async fn tp_to(
&self,
x: f32,
y: f32,
z: f32,
region_name: Option<String>,
lookat: Option<Option<f32>>,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvActionCallbacksDefault.TpToAsync(System.Single,System.Single,System.Single,System.String,System.Nullable{System.Single},System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.UnsitAsync(System.Threading.CancellationToken)`.
pub async fn unsit(
&self,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvActionCallbacksDefault.UnsitAsync(System.Threading.CancellationToken)",
)
}
}
impl libremetaverse_rlv::IRlvActionCallbacks for RlvActionCallbacksDefault {}
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.#ctor`.
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.AdjustHeightAsync(System.Single,System.Single,System.Single,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.AttachAsync(System.Collections.Generic.IReadOnlyList{LibreMetaverse.RLV.AttachmentRequest},System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.DetachAsync(System.Collections.Generic.IReadOnlyList{System.Guid},System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.RemOutfitAsync(System.Collections.Generic.IReadOnlyList{System.Guid},System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SendInstantMessageAsync(System.Guid,System.String,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SendReplyAsync(System.Int32,System.String,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SetCamFOVAsync(System.Single,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SetDebugAsync(System.String,System.String,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SetEnvAsync(System.String,System.String,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SetGroupAsync(System.Guid,System.String,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SetGroupAsync(System.String,System.String,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SetRotAsync(System.Single,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SitAsync(System.Guid,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.SitGroundAsync(System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.TpToAsync(System.Single,System.Single,System.Single,System.String,System.Nullable{System.Single},System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvActionCallbacksDefault.UnsitAsync(System.Threading.CancellationToken)`.
pub use crate::service::RlvActionCallbacksDefault;
/// C# type: `T:LibreMetaverse.RLV.RlvAttachmentPoint`.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
@@ -975,102 +779,20 @@ impl RlvBlacklist {
}
/// C# type: `T:LibreMetaverse.RLV.RlvCallbacksDefault`.
pub struct RlvCallbacksDefault;
impl RlvCallbacksDefault {
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.#ctor`.
pub fn new() -> Result<Self, crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.RLV.RlvCallbacksDefault.#ctor")
}
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.IsSittingAsync(System.Threading.CancellationToken)`.
pub async fn is_sitting(
&self,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvCallbacksDefault.IsSittingAsync(System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.ObjectExistsAsync(System.Guid,System.Threading.CancellationToken)`.
pub async fn object_exists(
&self,
object_id: libremetaverse_types::compat::Guid,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvCallbacksDefault.ObjectExistsAsync(System.Guid,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetActiveGroupNameAsync(System.Threading.CancellationToken)`.
pub async fn try_get_active_group_name(
&self,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(bool, String), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetActiveGroupNameAsync(System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetCameraSettingsAsync(System.Threading.CancellationToken)`.
pub async fn try_get_camera_settings(
&self,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(bool, Option<libremetaverse_rlv::CameraSettings>), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetCameraSettingsAsync(System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetDebugSettingValueAsync(System.String,System.Threading.CancellationToken)`.
pub async fn try_get_debug_setting_value(
&self,
setting_name: String,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(bool, String), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetDebugSettingValueAsync(System.String,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetEnvironmentSettingValueAsync(System.String,System.Threading.CancellationToken)`.
pub async fn try_get_environment_setting_value(
&self,
setting_name: String,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(bool, String), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetEnvironmentSettingValueAsync(System.String,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetInventoryMapAsync(System.Threading.CancellationToken)`.
pub async fn try_get_inventory_map(
&self,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(bool, Option<libremetaverse_rlv::InventoryMap>), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetInventoryMapAsync(System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetSharedFolderAsync(System.Threading.CancellationToken)`.
pub async fn try_get_shared_folder(
&self,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(bool, Option<libremetaverse_rlv::RlvSharedFolder>), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetSharedFolderAsync(System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetSitIdAsync(System.Threading.CancellationToken)`.
pub async fn try_get_sit_id(
&self,
cancellation_token: libremetaverse_types::compat::CancellationToken,
) -> Result<(bool, libremetaverse_types::compat::Guid), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetSitIdAsync(System.Threading.CancellationToken)",
)
}
}
impl libremetaverse_rlv::IRlvQueryCallbacks for RlvCallbacksDefault {}
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.#ctor`.
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.IsSittingAsync(System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.ObjectExistsAsync(System.Guid,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetActiveGroupNameAsync(System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetCameraSettingsAsync(System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetDebugSettingValueAsync(System.String,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetEnvironmentSettingValueAsync(System.String,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetInventoryMapAsync(System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetSharedFolderAsync(System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvCallbacksDefault.TryGetSitIdAsync(System.Threading.CancellationToken)`.
pub use crate::service::RlvCallbacksDefault;
/// C# type: `T:LibreMetaverse.RLV.RlvCommandProcessor`.
pub struct RlvCommandProcessor;
impl RlvCommandProcessor {}
pub use crate::service::RlvCommandProcessor;
/// C# type: `T:LibreMetaverse.RLV.RlvCommon`.
pub use crate::protocol::RlvCommon;
@@ -2066,183 +1788,27 @@ pub enum RlvRestrictionType {
}
/// C# type: `T:LibreMetaverse.RLV.RlvService`.
pub struct RlvService;
impl RlvService {
/// C# member: `F:LibreMetaverse.RLV.RlvService.RLVVersion`.
pub const RLV_VERSION: &'static str = "RestrainedLove viewer v3.4.3 (RLVa 2.4.2)";
/// C# member: `F:LibreMetaverse.RLV.RlvService.RLVVersionNum`.
pub const RLV_VERSION_NUM: &'static str = "2040213";
/// C# member: `M:LibreMetaverse.RLV.RlvService.#ctor(LibreMetaverse.RLV.IRlvQueryCallbacks,LibreMetaverse.RLV.IRlvActionCallbacks,System.Boolean)`.
pub fn new(
callbacks: Box<dyn libremetaverse_rlv::IRlvQueryCallbacks>,
action_callbacks: Box<dyn libremetaverse_rlv::IRlvActionCallbacks>,
enabled: bool,
) -> Result<Self, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvService.#ctor(LibreMetaverse.RLV.IRlvQueryCallbacks,LibreMetaverse.RLV.IRlvActionCallbacks,System.Boolean)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvService.ProcessInstantMessageAsync(System.String,System.Guid,System.Threading.CancellationToken)`.
pub async fn process_instant_message(
&self,
message: String,
sender_id: libremetaverse_types::compat::Guid,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvService.ProcessInstantMessageAsync(System.String,System.Guid,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvService.ProcessMessageAsync(System.String,System.Guid,System.String,System.Threading.CancellationToken)`.
pub async fn process_message(
&self,
message: String,
sender_id: libremetaverse_types::compat::Guid,
sender_name: String,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvService.ProcessMessageAsync(System.String,System.Guid,System.String,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvService.ReportInventoryOfferAcceptedAsync(System.String,System.Threading.CancellationToken)`.
pub async fn report_inventory_offer_accepted(
&self,
folder_path: String,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvService.ReportInventoryOfferAcceptedAsync(System.String,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvService.ReportInventoryOfferDeclinedAsync(System.String,System.Threading.CancellationToken)`.
pub async fn report_inventory_offer_declined(
&self,
folder_path: String,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvService.ReportInventoryOfferDeclinedAsync(System.String,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvService.ReportItemAttachedAsync(System.Guid,System.Boolean,LibreMetaverse.RLV.RlvAttachmentPoint,System.Threading.CancellationToken)`.
pub async fn report_item_attached(
&self,
object_folder_id: libremetaverse_types::compat::Guid,
is_shared: bool,
attachment_point: libremetaverse_rlv::RlvAttachmentPoint,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvService.ReportItemAttachedAsync(System.Guid,System.Boolean,LibreMetaverse.RLV.RlvAttachmentPoint,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvService.ReportItemDetachedAsync(System.Guid,System.Guid,System.Guid,System.Boolean,LibreMetaverse.RLV.RlvAttachmentPoint,System.Threading.CancellationToken)`.
pub async fn report_item_detached(
&self,
item_id: libremetaverse_types::compat::Guid,
prim_id: libremetaverse_types::compat::Guid,
object_folder_id: libremetaverse_types::compat::Guid,
is_shared: bool,
attachment_point: libremetaverse_rlv::RlvAttachmentPoint,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvService.ReportItemDetachedAsync(System.Guid,System.Guid,System.Guid,System.Boolean,LibreMetaverse.RLV.RlvAttachmentPoint,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvService.ReportItemUnwornAsync(System.Guid,System.Guid,System.Boolean,LibreMetaverse.RLV.RlvWearableType,System.Threading.CancellationToken)`.
pub async fn report_item_unworn(
&self,
item_id: libremetaverse_types::compat::Guid,
object_folder_id: libremetaverse_types::compat::Guid,
is_shared: bool,
wearable_type: libremetaverse_rlv::RlvWearableType,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvService.ReportItemUnwornAsync(System.Guid,System.Guid,System.Boolean,LibreMetaverse.RLV.RlvWearableType,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvService.ReportItemWornAsync(System.Guid,System.Boolean,LibreMetaverse.RLV.RlvWearableType,System.Threading.CancellationToken)`.
pub async fn report_item_worn(
&self,
object_folder_id: libremetaverse_types::compat::Guid,
is_shared: bool,
wearable_type: libremetaverse_rlv::RlvWearableType,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvService.ReportItemWornAsync(System.Guid,System.Boolean,LibreMetaverse.RLV.RlvWearableType,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvService.ReportSendPublicMessageAsync(System.String,System.Threading.CancellationToken)`.
pub async fn report_send_public_message(
&self,
message: String,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvService.ReportSendPublicMessageAsync(System.String,System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvService.ReportSitAsync(System.Nullable{System.Guid},System.Threading.CancellationToken)`.
pub async fn report_sit(
&self,
object_id: Option<Option<libremetaverse_types::compat::Guid>>,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvService.ReportSitAsync(System.Nullable{System.Guid},System.Threading.CancellationToken)",
)
}
/// C# member: `M:LibreMetaverse.RLV.RlvService.ReportUnsitAsync(System.Nullable{System.Guid},System.Threading.CancellationToken)`.
pub async fn report_unsit(
&self,
object_id: Option<Option<libremetaverse_types::compat::Guid>>,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.RLV.RlvService.ReportUnsitAsync(System.Nullable{System.Guid},System.Threading.CancellationToken)",
)
}
/// C# member: `P:LibreMetaverse.RLV.RlvService.Blacklist`.
pub fn blacklist(&self) -> libremetaverse_rlv::RlvBlacklist {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.RLV.RlvService.Blacklist")
}
/// C# member: `P:LibreMetaverse.RLV.RlvService.Commands`.
pub fn commands(&self) -> libremetaverse_rlv::RlvCommandProcessor {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.RLV.RlvService.Commands")
}
/// C# member: `P:LibreMetaverse.RLV.RlvService.EnableInstantMessageProcessing`.
pub fn enable_instant_message_processing(&self) -> bool {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.RLV.RlvService.EnableInstantMessageProcessing"
)
}
/// Setter for C# member: `P:LibreMetaverse.RLV.RlvService.EnableInstantMessageProcessing`.
pub fn set_enable_instant_message_processing(&mut self, value: bool) {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.RLV.RlvService.EnableInstantMessageProcessing"
)
}
/// C# member: `P:LibreMetaverse.RLV.RlvService.Enabled`.
pub fn enabled(&self) -> bool {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.RLV.RlvService.Enabled")
}
/// Setter for C# member: `P:LibreMetaverse.RLV.RlvService.Enabled`.
pub fn set_enabled(&mut self, value: bool) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.RLV.RlvService.Enabled")
}
/// C# member: `P:LibreMetaverse.RLV.RlvService.Permissions`.
pub fn permissions(&self) -> libremetaverse_rlv::RlvPermissionsService {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.RLV.RlvService.Permissions")
}
/// C# member: `P:LibreMetaverse.RLV.RlvService.Restrictions`.
pub fn restrictions(&self) -> libremetaverse_rlv::RlvRestrictionManager {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.RLV.RlvService.Restrictions")
}
}
/// C# member: `F:LibreMetaverse.RLV.RlvService.RLVVersion`.
/// C# member: `F:LibreMetaverse.RLV.RlvService.RLVVersionNum`.
/// C# member: `M:LibreMetaverse.RLV.RlvService.#ctor(LibreMetaverse.RLV.IRlvQueryCallbacks,LibreMetaverse.RLV.IRlvActionCallbacks,System.Boolean)`.
/// C# member: `M:LibreMetaverse.RLV.RlvService.ProcessInstantMessageAsync(System.String,System.Guid,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvService.ProcessMessageAsync(System.String,System.Guid,System.String,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvService.ReportInventoryOfferAcceptedAsync(System.String,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvService.ReportInventoryOfferDeclinedAsync(System.String,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvService.ReportItemAttachedAsync(System.Guid,System.Boolean,LibreMetaverse.RLV.RlvAttachmentPoint,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvService.ReportItemDetachedAsync(System.Guid,System.Guid,System.Guid,System.Boolean,LibreMetaverse.RLV.RlvAttachmentPoint,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvService.ReportItemUnwornAsync(System.Guid,System.Guid,System.Boolean,LibreMetaverse.RLV.RlvWearableType,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvService.ReportItemWornAsync(System.Guid,System.Boolean,LibreMetaverse.RLV.RlvWearableType,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvService.ReportSendPublicMessageAsync(System.String,System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvService.ReportSitAsync(System.Nullable{System.Guid},System.Threading.CancellationToken)`.
/// C# member: `M:LibreMetaverse.RLV.RlvService.ReportUnsitAsync(System.Nullable{System.Guid},System.Threading.CancellationToken)`.
/// C# member: `P:LibreMetaverse.RLV.RlvService.Blacklist`.
/// C# member: `P:LibreMetaverse.RLV.RlvService.Commands`.
/// C# member: `P:LibreMetaverse.RLV.RlvService.EnableInstantMessageProcessing`.
/// C# member: `P:LibreMetaverse.RLV.RlvService.Enabled`.
/// C# member: `P:LibreMetaverse.RLV.RlvService.Permissions`.
/// C# member: `P:LibreMetaverse.RLV.RlvService.Restrictions`.
pub use crate::service::RlvService;
/// C# type: `T:LibreMetaverse.RLV.RlvSharedFolder`.
pub use crate::state::RlvSharedFolder;

View File

@@ -4,6 +4,7 @@ extern crate self as libremetaverse_rlv;
mod generated;
mod protocol;
mod service;
mod state;
pub use generated::*;

View File

@@ -1325,15 +1325,48 @@ fn parse_i32(value: &str) -> Option<i32> {
}
fn parse_guid(value: &str) -> Result<Guid, RlvParseErrorKind> {
UUID::parse(value.to_owned())
.map(|value| value.guid())
.map_err(|_| RlvParseErrorKind::InvalidUuid)
let digits = value
.bytes()
.filter(|byte| *byte != b'-')
.collect::<Vec<_>>();
if digits.len() != 32 {
return Err(RlvParseErrorKind::InvalidUuid);
}
let mut bytes = [0u8; 16];
for (index, pair) in digits.chunks_exact(2).enumerate() {
let digit = |byte| match byte {
b'0'..=b'9' => Some(byte - b'0'),
b'a'..=b'f' => Some(byte - b'a' + 10),
b'A'..=b'F' => Some(byte - b'A' + 10),
_ => None,
};
let high = digit(pair[0]).ok_or(RlvParseErrorKind::InvalidUuid)?;
let low = digit(pair[1]).ok_or(RlvParseErrorKind::InvalidUuid)?;
bytes[index] = high << 4 | low;
}
Ok(Guid(bytes))
}
fn guid_to_string(value: Guid) -> String {
UUID::new_with_guid(value).map_or_else(
|_| "00000000-0000-0000-0000-000000000000".to_owned(),
|value| value.to_string(),
let bytes = value.0;
format!(
"{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
bytes[0],
bytes[1],
bytes[2],
bytes[3],
bytes[4],
bytes[5],
bytes[6],
bytes[7],
bytes[8],
bytes[9],
bytes[10],
bytes[11],
bytes[12],
bytes[13],
bytes[14],
bytes[15],
)
}
@@ -1341,7 +1374,9 @@ fn value_to_object(value: RlvValue) -> Object {
match value {
RlvValue::Integer(value) => Object::Integer(value),
RlvValue::Real(value) => Object::Real(f64::from(value)),
RlvValue::Uuid(value) => UUID::new_with_guid(value).map_or(Object::Undefined, Object::UUID),
RlvValue::Uuid(value) => {
UUID::parse(guid_to_string(value)).map_or(Object::Undefined, Object::UUID)
}
RlvValue::String(value) => Object::String(value),
RlvValue::AttachmentPoint(value) => Object::Integer(value as i32),
RlvValue::WearableType(value) => Object::Integer(value as i32),

File diff suppressed because it is too large Load Diff

View File

@@ -20,9 +20,10 @@ use libremetaverse_types::compat::{
use libremetaverse_types::{Error, UUID};
use crate::{
RlvAttachmentPoint, RlvGestureState, RlvPermissionsServiceHoverTextLocation,
RlvPermissionsServiceObjectLocation, RlvPermissionsServiceTouchLocation, RlvRestriction,
RlvRestrictionType, RlvValue, RlvWearableType, restriction_name,
IRlvActionCallbacks, RlvAttachmentPoint, RlvGestureState,
RlvPermissionsServiceHoverTextLocation, RlvPermissionsServiceObjectLocation,
RlvPermissionsServiceTouchLocation, RlvRestriction, RlvRestrictionType, RlvValue,
RlvWearableType, restriction_name,
};
/// Maximum number of folders accepted in one immutable inventory snapshot.
@@ -970,6 +971,7 @@ struct RestrictionState {
struct RestrictionManagerInner {
state: RwLock<RestrictionState>,
action_callbacks: RwLock<Option<Arc<dyn IRlvActionCallbacks>>>,
handlers: Arc<Mutex<BTreeMap<u64, EventHandler<RestrictionUpdatedEventArgs>>>>,
next_handler: AtomicU64,
}
@@ -1003,12 +1005,17 @@ impl RlvRestrictionManager {
Self {
inner: Arc::new(RestrictionManagerInner {
state: RwLock::new(RestrictionState::default()),
action_callbacks: RwLock::new(None),
handlers: Arc::new(Mutex::new(BTreeMap::new())),
next_handler: AtomicU64::new(1),
}),
}
}
pub(crate) fn set_action_callbacks(&self, callbacks: Arc<dyn IRlvActionCallbacks>) {
*write_lock(&self.inner.action_callbacks) = Some(callbacks);
}
/// Replaces the shared-inventory snapshot used for folder-lock decisions.
pub fn set_inventory_map(&self, inventory: Option<InventoryMap>) {
let mut state = write_lock(&self.inner.state);
@@ -1215,10 +1222,58 @@ impl RlvRestrictionManager {
token.throw_if_cancellation_requested()?;
}
self.emit(RestrictionUpdatedEventArgs {
restriction,
restriction: restriction.clone(),
is_new: false,
is_deleted: true,
});
self.notify_removed_restriction(&restriction, cancellation_token.clone())
.await?;
}
}
Ok(())
}
async fn notify_removed_restriction(
&self,
restriction: &RlvRestriction,
cancellation_token: Option<CancellationToken>,
) -> Result<(), Error> {
let callbacks = read_lock(&self.inner.action_callbacks).clone();
let Some(callbacks) = callbacks else {
return Ok(());
};
let listeners = self.restrictions_by_type(RlvRestrictionType::Notify);
let behavior = restriction_name(restriction.original_behavior());
let mut message = behavior.to_owned();
if !restriction.values().is_empty() {
message.push(':');
message.push_str(
&restriction
.values()
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(";"),
);
}
message.push_str("=y");
let token = cancellation_token.unwrap_or_default();
for listener in listeners {
token.throw_if_cancellation_requested()?;
let [RlvValue::Integer(channel), rest @ ..] = listener.values() else {
continue;
};
let filter = match rest.first() {
Some(RlvValue::String(value)) => value.as_str(),
_ => "",
};
if behavior
.to_ascii_lowercase()
.contains(&filter.to_ascii_lowercase())
{
callbacks
.send_reply(*channel, format!("/{message}"), token.clone())
.await?;
}
}
Ok(())