Implement native RLV state and permissions (#79)
Some checks failed
Native code generation / deterministic (push) Failing after 2m1s
Imaging and meshing gate / native (push) Successful in 5m22s
JPEG 2000 feature / linux (push) Successful in 2m45s
Native Rust workspace compile / compile (push) Failing after 6m12s
Skia feature / linux (push) Successful in 30m44s
Some checks failed
Native code generation / deterministic (push) Failing after 2m1s
Imaging and meshing gate / native (push) Successful in 5m22s
JPEG 2000 feature / linux (push) Successful in 2m45s
Native Rust workspace compile / compile (push) Failing after 6m12s
Skia feature / linux (push) Successful in 30m44s
This commit is contained in:
@@ -10,6 +10,7 @@ on:
|
|||||||
- "tools/check_milestone_10_issue_76.py"
|
- "tools/check_milestone_10_issue_76.py"
|
||||||
- "tools/check_milestone_10_issue_77.py"
|
- "tools/check_milestone_10_issue_77.py"
|
||||||
- "tools/check_milestone_10_issue_78.py"
|
- "tools/check_milestone_10_issue_78.py"
|
||||||
|
- "tools/check_milestone_10_issue_79.py"
|
||||||
- "**/*.rs"
|
- "**/*.rs"
|
||||||
- "**/Cargo.toml"
|
- "**/Cargo.toml"
|
||||||
- "Cargo.lock"
|
- "Cargo.lock"
|
||||||
@@ -22,6 +23,7 @@ on:
|
|||||||
- "tools/check_milestone_10_issue_76.py"
|
- "tools/check_milestone_10_issue_76.py"
|
||||||
- "tools/check_milestone_10_issue_77.py"
|
- "tools/check_milestone_10_issue_77.py"
|
||||||
- "tools/check_milestone_10_issue_78.py"
|
- "tools/check_milestone_10_issue_78.py"
|
||||||
|
- "tools/check_milestone_10_issue_79.py"
|
||||||
- "**/*.rs"
|
- "**/*.rs"
|
||||||
- "**/Cargo.toml"
|
- "**/Cargo.toml"
|
||||||
- "Cargo.lock"
|
- "Cargo.lock"
|
||||||
@@ -60,6 +62,7 @@ jobs:
|
|||||||
python3 tools/check_milestone_10_issue_76.py
|
python3 tools/check_milestone_10_issue_76.py
|
||||||
python3 tools/check_milestone_10_issue_77.py
|
python3 tools/check_milestone_10_issue_77.py
|
||||||
python3 tools/check_milestone_10_issue_78.py
|
python3 tools/check_milestone_10_issue_78.py
|
||||||
|
python3 tools/check_milestone_10_issue_79.py
|
||||||
- name: Test the complete native world milestone
|
- name: Test the complete native world milestone
|
||||||
run: python3 tools/test_milestone_09.py
|
run: python3 tools/test_milestone_09.py
|
||||||
- name: Compile every workspace target with bounded memory
|
- name: Compile every workspace target with bounded memory
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -463,14 +463,17 @@ reports decode allocations and timing without replacing correctness gates. The
|
|||||||
format, ownership, error, and tangent-output contracts are documented in the
|
format, ownership, error, and tangent-output contracts are documented in the
|
||||||
[`MeshFoundry` guide](crates/libremetaverse-rendering-mesh-foundry/README.md).
|
[`MeshFoundry` guide](crates/libremetaverse-rendering-mesh-foundry/README.md).
|
||||||
|
|
||||||
The native RLV protocol layer parses bounded chat messages into typed clear,
|
The native RLV layer parses bounded chat messages into typed clear, action,
|
||||||
action, restriction, and query directives without changing client state. It
|
restriction, and query directives, then provides independent thread-safe
|
||||||
|
restriction, inventory-map, folder-lock, camera, blacklist, and permission
|
||||||
|
providers without network I/O. It
|
||||||
preserves source casing, folder paths, separators, numeric channels, UUIDs, and
|
preserves source casing, folder paths, separators, numeric channels, UUIDs, and
|
||||||
attachment/wearable aliases while exposing canonical behavior names and
|
attachment/wearable aliases while exposing canonical behavior names and
|
||||||
source-located typed errors. All 119 restriction names from the pinned
|
source-located typed errors. All 119 restriction names from the pinned
|
||||||
LibreMetaverse snapshot are covered, and deterministic malformed-input
|
LibreMetaverse snapshot are covered, and deterministic malformed-input
|
||||||
mutations exercise the parser without an external fuzzing runtime. State,
|
mutations exercise the parser without an external fuzzing runtime. Immutable
|
||||||
camera, inventory, callback, and transport effects remain explicit consumers
|
snapshots and post-lock update events make concurrent policy reads
|
||||||
of this pure layer. The grammar, normalization rules, limits, and ownership
|
deterministic; callback execution and transport effects remain explicit
|
||||||
boundary are documented in the
|
consumers of the layer. The grammar, state precedence, normalization rules,
|
||||||
|
limits, and ownership boundary are documented in the
|
||||||
[`RLV protocol guide`](crates/libremetaverse-rlv/README.md).
|
[`RLV protocol guide`](crates/libremetaverse-rlv/README.md).
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Generated by `python3 tools/generate_api_shims.py`; do not edit by hand.
|
|||||||
| `LibreMetaverse.Imaging.Skia` | 1 | 3 | native implementation: 1 type / 3 members; no generated shims remain |
|
| `LibreMetaverse.Imaging.Skia` | 1 | 3 | native implementation: 1 type / 3 members; no generated shims remain |
|
||||||
| `LibreMetaverse.LslTools` | 164 | 768 | callable failure-only shim |
|
| `LibreMetaverse.LslTools` | 164 | 768 | callable failure-only shim |
|
||||||
| `LibreMetaverse.PrimMesher` | 17 | 207 | native implementation: 15 types / 200 members; remaining surface is callable failure-only shims |
|
| `LibreMetaverse.PrimMesher` | 17 | 207 | native implementation: 15 types / 200 members; remaining surface is callable failure-only shims |
|
||||||
| `LibreMetaverse.RLV` | 28 | 499 | native implementation: 2 types / 11 members; remaining surface is callable failure-only shims |
|
| `LibreMetaverse.RLV` | 28 | 499 | native implementation: 12 types / 147 members; remaining surface is callable failure-only shims |
|
||||||
| `LibreMetaverse.Rendering.MeshFoundry` | 1 | 14 | native implementation: 1 type / 14 members; no generated shims remain |
|
| `LibreMetaverse.Rendering.MeshFoundry` | 1 | 14 | native implementation: 1 type / 14 members; no generated shims remain |
|
||||||
| `LibreMetaverse.Rendering.Simple` | 1 | 6 | native implementation: 1 type / 6 members; no generated shims remain |
|
| `LibreMetaverse.Rendering.Simple` | 1 | 6 | native implementation: 1 type / 6 members; no generated shims remain |
|
||||||
| `LibreMetaverse.StructuredData` | 16 | 295 | native implementation: 16 types / 295 members; no generated shims remain |
|
| `LibreMetaverse.StructuredData` | 16 | 295 | native implementation: 16 types / 295 members; no generated shims remain |
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ edition.workspace = true
|
|||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
description = "Bounded native RLV protocol parsing and compatibility types for MetaCrate"
|
description = "Bounded native RLV protocol, state, inventory, lock, camera, and permission layer"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libremetaverse-types = { path = "../libremetaverse-types" }
|
libremetaverse-types = { path = "../libremetaverse-types" }
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
# Native RLV protocol layer
|
# Native RLV protocol layer
|
||||||
|
|
||||||
`libremetaverse-rlv` implements the side-effect-free protocol boundary for the
|
`libremetaverse-rlv` implements the protocol and pure state boundary for the
|
||||||
Restrained Love Viewer support in the pinned LibreMetaverse snapshot. The
|
Restrained Love Viewer support in the pinned LibreMetaverse snapshot. The
|
||||||
parser converts one bounded chat message into typed commands. It does not
|
side-effect-free parser converts one bounded chat message into typed commands.
|
||||||
mutate restrictions, move the camera, touch inventory, send replies, or invoke
|
Independent, thread-safe managers then store restrictions and evaluate
|
||||||
callbacks; those stateful responsibilities are separate milestone work that
|
inventory, folder locks, camera limits, blacklists, and permissions without
|
||||||
consumes this layer.
|
performing network I/O. Command execution, viewer callbacks, replies, and
|
||||||
|
`GridClient` integration remain separate milestone work that consumes this
|
||||||
|
layer.
|
||||||
|
|
||||||
## Message contract
|
## Message contract
|
||||||
|
|
||||||
@@ -57,6 +59,33 @@ insertion mutations of actions, restrictions, queries, UUIDs, aliases, and
|
|||||||
multi-command messages. Every mutation must return a value or a positioned
|
multi-command messages. Every mutation must return a value or a positioned
|
||||||
error without panicking. The limits apply before large parser allocations.
|
error without panicking. The limits apply before large parser allocations.
|
||||||
|
|
||||||
|
## State, inventory, and permission contract
|
||||||
|
|
||||||
|
`RlvSharedFolder` and `RlvInventoryItem` preserve the reference semantics of
|
||||||
|
their C# counterparts. `InventoryMap` walks a bounded shared-inventory tree and
|
||||||
|
publishes immutable dictionary/list membership snapshots. Path lookup handles
|
||||||
|
hidden/private prefixes, exact names containing forward slashes, and the
|
||||||
|
reference implementation's longest matching segment rule. Lookup by item,
|
||||||
|
attached prim, attachment point, and wearable type does not call a client or
|
||||||
|
inventory service.
|
||||||
|
|
||||||
|
`RlvRestrictionManager` deduplicates exact restrictions, retains deterministic
|
||||||
|
insertion order, removes all state for selected object sources, and rebuilds
|
||||||
|
immutable locked-folder snapshots when either restrictions or inventory
|
||||||
|
changes. Recursive and non-recursive attach/detach locks support sender-item,
|
||||||
|
attachment, wearable, and path targets plus their exception variants. Update
|
||||||
|
handlers run only after manager locks are released, so callbacks can safely
|
||||||
|
query the manager again. Poisoned synchronization primitives recover their
|
||||||
|
owned state instead of making later reads fail.
|
||||||
|
|
||||||
|
`RlvPermissionsService` evaluates simple restrictions, secure and explicit
|
||||||
|
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.
|
||||||
|
|
||||||
## Reproducible verification
|
## Reproducible verification
|
||||||
|
|
||||||
Run the issue-owned checks with one build job:
|
Run the issue-owned checks with one build job:
|
||||||
@@ -64,9 +93,11 @@ Run the issue-owned checks with one build job:
|
|||||||
```sh
|
```sh
|
||||||
CARGO_BUILD_JOBS=1 cargo test -p libremetaverse-rlv --locked
|
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_common_semantics --locked
|
||||||
|
CARGO_BUILD_JOBS=1 cargo test -p libremetaverse-compat-tests --test rlv_inventory_map_semantics --locked
|
||||||
CARGO_BUILD_JOBS=1 cargo clippy -p libremetaverse-rlv --all-targets --locked -- -D warnings
|
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
|
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_78.py
|
||||||
|
python3 tools/check_milestone_10_issue_79.py
|
||||||
```
|
```
|
||||||
|
|
||||||
These commands are cross-platform. The Gitea workflow runs the audit and
|
These commands are cross-platform. The Gitea workflow runs the audit and
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,9 @@ extern crate self as libremetaverse_rlv;
|
|||||||
|
|
||||||
mod generated;
|
mod generated;
|
||||||
mod protocol;
|
mod protocol;
|
||||||
|
mod state;
|
||||||
|
|
||||||
pub use generated::*;
|
pub use generated::*;
|
||||||
pub use libremetaverse_types::Error;
|
pub use libremetaverse_types::Error;
|
||||||
pub use protocol::*;
|
pub use protocol::*;
|
||||||
|
pub use state::*;
|
||||||
|
|||||||
2174
crates/libremetaverse-rlv/src/state.rs
Normal file
2174
crates/libremetaverse-rlv/src/state.rs
Normal file
File diff suppressed because it is too large
Load Diff
602
crates/libremetaverse-rlv/tests/state_semantics.rs
Normal file
602
crates/libremetaverse-rlv/tests/state_semantics.rs
Normal file
@@ -0,0 +1,602 @@
|
|||||||
|
#![allow(clippy::float_cmp)] // Protocol fixture values are exactly representable.
|
||||||
|
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::task::{Context, Poll, Waker};
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
use libremetaverse_rlv::{
|
||||||
|
CameraSettings, InventoryMap, RlvAttachmentPoint, RlvBlacklist, RlvInventoryItem,
|
||||||
|
RlvPermissionsService, RlvPermissionsServiceHoverTextLocation,
|
||||||
|
RlvPermissionsServiceObjectLocation, RlvPermissionsServiceTouchLocation, RlvRestriction,
|
||||||
|
RlvRestrictionManager, RlvRestrictionType, RlvSharedFolder, RlvValue, RlvWearableType,
|
||||||
|
};
|
||||||
|
use libremetaverse_types::compat::Guid;
|
||||||
|
|
||||||
|
fn guid(byte: u8) -> Guid {
|
||||||
|
Guid([byte; 16])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restriction(behavior: RlvRestrictionType, sender: u8, values: Vec<RlvValue>) -> RlvRestriction {
|
||||||
|
RlvRestriction::from_values(behavior, guid(sender), format!("sender-{sender}"), values)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_ready<F: Future>(future: F) -> F::Output {
|
||||||
|
let waker = Waker::noop();
|
||||||
|
let mut context = Context::from_waker(waker);
|
||||||
|
let mut future = std::pin::pin!(future);
|
||||||
|
match future.as_mut().poll(&mut context) {
|
||||||
|
Poll::Ready(output) => output,
|
||||||
|
Poll::Pending => panic!("pure state future unexpectedly yielded"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn manager_deduplicates_updates_and_dispatches_after_unlocking() {
|
||||||
|
let manager = RlvRestrictionManager::new();
|
||||||
|
let events = Arc::new(Mutex::new(Vec::new()));
|
||||||
|
let captured = Arc::clone(&events);
|
||||||
|
let callback_manager = manager.clone();
|
||||||
|
let _subscription = manager.subscribe_restriction_updated(Some(Arc::new(move |event| {
|
||||||
|
// A re-entrant read would deadlock if callbacks ran under the state lock.
|
||||||
|
let count = callback_manager
|
||||||
|
.find_restrictions(None, None)
|
||||||
|
.unwrap()
|
||||||
|
.len();
|
||||||
|
captured
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.push((event.is_new(), event.is_deleted(), count));
|
||||||
|
})));
|
||||||
|
|
||||||
|
let fly = restriction(RlvRestrictionType::Fly, 1, vec![]);
|
||||||
|
assert!(manager.add_restriction(fly.clone()).unwrap());
|
||||||
|
assert!(!manager.add_restriction(fly.clone()).unwrap());
|
||||||
|
assert_eq!(
|
||||||
|
manager.get_tracked_prim_ids().unwrap().collect::<Vec<_>>(),
|
||||||
|
[guid(1)]
|
||||||
|
);
|
||||||
|
assert!(manager.remove_restriction(&fly));
|
||||||
|
assert!(!manager.remove_restriction(&fly));
|
||||||
|
assert_eq!(
|
||||||
|
*events.lock().unwrap(),
|
||||||
|
[(true, false, 1), (false, true, 0)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn manager_handles_concurrent_independent_sources_deterministically() {
|
||||||
|
let manager = RlvRestrictionManager::new();
|
||||||
|
let mut workers = Vec::new();
|
||||||
|
for sender in 1..=24 {
|
||||||
|
let worker_manager = manager.clone();
|
||||||
|
workers.push(thread::spawn(move || {
|
||||||
|
worker_manager
|
||||||
|
.add_restriction(restriction(RlvRestrictionType::Jump, sender, vec![]))
|
||||||
|
.unwrap()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
for worker in workers {
|
||||||
|
assert!(worker.join().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let restrictions = manager
|
||||||
|
.get_restrictions_by_type(RlvRestrictionType::Jump)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(restrictions.len(), 24);
|
||||||
|
let mut tracked = manager.get_tracked_prim_ids().unwrap().collect::<Vec<_>>();
|
||||||
|
tracked.sort_by_key(|value| value.0);
|
||||||
|
assert_eq!(tracked, (1..=24).map(guid).collect::<Vec<_>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn concurrent_lock_rebuilds_cannot_publish_a_stale_revision() {
|
||||||
|
let root = RlvSharedFolder::new(guid(1), "#RLV".into()).unwrap();
|
||||||
|
let first = root.add_child(guid(2), "first".into()).unwrap();
|
||||||
|
let second = root.add_child(guid(3), "second".into()).unwrap();
|
||||||
|
first
|
||||||
|
.add_item(
|
||||||
|
guid(4),
|
||||||
|
"first attachment".into(),
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
Some(Some(guid(40))),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
second
|
||||||
|
.add_item(
|
||||||
|
guid(5),
|
||||||
|
"second attachment".into(),
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
Some(Some(guid(50))),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let manager = RlvRestrictionManager::new();
|
||||||
|
manager.set_inventory_map(Some(InventoryMap::new(root, vec![]).unwrap()));
|
||||||
|
let barrier = Arc::new(std::sync::Barrier::new(3));
|
||||||
|
let mut workers = Vec::new();
|
||||||
|
for sender in [40, 50] {
|
||||||
|
let worker_manager = manager.clone();
|
||||||
|
let worker_barrier = Arc::clone(&barrier);
|
||||||
|
workers.push(thread::spawn(move || {
|
||||||
|
worker_barrier.wait();
|
||||||
|
worker_manager
|
||||||
|
.add_restriction(restriction(RlvRestrictionType::DetachThis, sender, vec![]))
|
||||||
|
.unwrap();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
barrier.wait();
|
||||||
|
for worker in workers {
|
||||||
|
worker.join().unwrap();
|
||||||
|
}
|
||||||
|
let locked = manager.get_locked_folders().unwrap();
|
||||||
|
assert_eq!(locked.len(), 2);
|
||||||
|
assert!(!locked[&first.id()].can_detach());
|
||||||
|
assert!(!locked[&second.id()].can_detach());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn source_removal_counts_repeated_inputs_before_deduplication() {
|
||||||
|
let manager = RlvRestrictionManager::new();
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(RlvRestrictionType::Fly, 1, vec![]))
|
||||||
|
.unwrap();
|
||||||
|
let repeated = std::iter::repeat_n(guid(1), 100_001);
|
||||||
|
assert!(poll_ready(manager.remove_restrictions_for_objects(Box::new(repeated), None)).is_err());
|
||||||
|
assert_eq!(manager.find_restrictions(None, None).unwrap().len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn blacklist_is_case_insensitive_sorted_and_thread_safe() {
|
||||||
|
let blacklist = RlvBlacklist::new();
|
||||||
|
let mut workers = Vec::new();
|
||||||
|
for behavior in ["Fly", "JUMP", "sit", "CamZoomMax"] {
|
||||||
|
let worker_blacklist = blacklist.clone();
|
||||||
|
workers.push(thread::spawn(move || {
|
||||||
|
worker_blacklist
|
||||||
|
.blacklist_behavior(behavior.to_owned())
|
||||||
|
.unwrap();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
for worker in workers {
|
||||||
|
worker.join().unwrap();
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
blacklist.get_blacklist().unwrap(),
|
||||||
|
["camzoommax", "fly", "jump", "sit"]
|
||||||
|
);
|
||||||
|
assert!(blacklist.is_blacklisted("fLy".into()).unwrap());
|
||||||
|
blacklist.un_blacklist_behavior("FLY".into()).unwrap();
|
||||||
|
assert!(!blacklist.is_blacklisted("fly".into()).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn camera_restrictions_apply_minimum_maximum_alias_and_last_value_rules() {
|
||||||
|
let manager = RlvRestrictionManager::new();
|
||||||
|
for (sender, value) in [(4, 0.4), (7, 0.7)] {
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(
|
||||||
|
RlvRestrictionType::CamZoomMin,
|
||||||
|
sender,
|
||||||
|
vec![RlvValue::Real(value)],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
for (sender, value) in [(20, 2.0), (12, 1.2)] {
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(
|
||||||
|
RlvRestrictionType::CamZoomMax,
|
||||||
|
sender,
|
||||||
|
vec![RlvValue::Real(value)],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(
|
||||||
|
RlvRestrictionType::CamDistMin,
|
||||||
|
31,
|
||||||
|
vec![RlvValue::Real(3.0)],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(
|
||||||
|
RlvRestrictionType::CamDrawColor,
|
||||||
|
32,
|
||||||
|
vec![
|
||||||
|
RlvValue::Real(0.0),
|
||||||
|
RlvValue::Real(0.5),
|
||||||
|
RlvValue::Real(1.0),
|
||||||
|
],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(
|
||||||
|
RlvRestrictionType::CamDrawColor,
|
||||||
|
33,
|
||||||
|
vec![
|
||||||
|
RlvValue::Real(1.0),
|
||||||
|
RlvValue::Real(1.5),
|
||||||
|
RlvValue::Real(-1.0),
|
||||||
|
],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(RlvRestrictionType::CamUnlock, 34, vec![]))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let camera = RlvPermissionsService::new(manager)
|
||||||
|
.get_camera_restrictions()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(camera.zoom_min().flatten(), Some(0.7));
|
||||||
|
assert_eq!(camera.zoom_max().flatten(), Some(1.2));
|
||||||
|
assert_eq!(camera.av_dist_min().flatten(), Some(3.0));
|
||||||
|
assert_eq!(camera.draw_color().flatten().unwrap().0, [0.5, 0.75, 0.5]);
|
||||||
|
assert!(camera.is_locked());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn camera_settings_preserve_constructor_field_order() {
|
||||||
|
let settings = CameraSettings::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0).unwrap();
|
||||||
|
assert_eq!(settings.av_dist_min(), 1.0);
|
||||||
|
assert_eq!(settings.av_dist_max(), 2.0);
|
||||||
|
assert_eq!(settings.fov_min(), 3.0);
|
||||||
|
assert_eq!(settings.fov_max(), 4.0);
|
||||||
|
assert_eq!(settings.zoom_min(), 5.0);
|
||||||
|
assert_eq!(settings.current_fov(), 6.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn permission_exceptions_are_targeted_and_secure_rules_take_precedence() {
|
||||||
|
let manager = RlvRestrictionManager::new();
|
||||||
|
let permissions = RlvPermissionsService::new(manager.clone());
|
||||||
|
let allowed_user = guid(90);
|
||||||
|
let other_user = guid(91);
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(RlvRestrictionType::SendIm, 1, vec![]))
|
||||||
|
.unwrap();
|
||||||
|
assert!(
|
||||||
|
!permissions
|
||||||
|
.can_send_im("hello".into(), Some(Some(allowed_user)), None)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(
|
||||||
|
RlvRestrictionType::SendIm,
|
||||||
|
1,
|
||||||
|
vec![RlvValue::Uuid(allowed_user)],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
assert!(
|
||||||
|
permissions
|
||||||
|
.can_send_im("hello".into(), Some(Some(allowed_user)), None)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!permissions
|
||||||
|
.can_send_im("hello".into(), Some(Some(other_user)), None)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(RlvRestrictionType::SendImSec, 2, vec![]))
|
||||||
|
.unwrap();
|
||||||
|
assert!(
|
||||||
|
!permissions
|
||||||
|
.can_send_im("hello".into(), Some(Some(allowed_user)), None)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_interaction_and_location_permissions_compose() {
|
||||||
|
let manager = RlvRestrictionManager::new();
|
||||||
|
let permissions = RlvPermissionsService::new(manager.clone());
|
||||||
|
assert!(permissions.can_fly().unwrap());
|
||||||
|
assert!(permissions.can_sit().unwrap());
|
||||||
|
assert!(permissions.can_rez().unwrap());
|
||||||
|
assert!(
|
||||||
|
permissions
|
||||||
|
.can_edit(RlvPermissionsServiceObjectLocation::RezzedInWorld, None)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
let interact = restriction(RlvRestrictionType::Interact, 1, vec![]);
|
||||||
|
manager.add_restriction(interact.clone()).unwrap();
|
||||||
|
assert!(!permissions.can_sit().unwrap());
|
||||||
|
assert!(!permissions.can_rez().unwrap());
|
||||||
|
assert!(
|
||||||
|
!permissions
|
||||||
|
.can_edit(RlvPermissionsServiceObjectLocation::Hud, None)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert!(manager.remove_restriction(&interact));
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(RlvRestrictionType::EditWorld, 2, vec![]))
|
||||||
|
.unwrap();
|
||||||
|
assert!(
|
||||||
|
!permissions
|
||||||
|
.can_edit(RlvPermissionsServiceObjectLocation::RezzedInWorld, None)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
permissions
|
||||||
|
.can_edit(RlvPermissionsServiceObjectLocation::Attached, None)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn distance_and_channel_outputs_apply_defaults_minima_and_precedence() {
|
||||||
|
let manager = RlvRestrictionManager::new();
|
||||||
|
let permissions = RlvPermissionsService::new(manager.clone());
|
||||||
|
let mut distance = -1.0;
|
||||||
|
assert!(!permissions.can_sit_tp(&mut distance).unwrap());
|
||||||
|
assert_eq!(distance, 1.5);
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(
|
||||||
|
RlvRestrictionType::SitTp,
|
||||||
|
1,
|
||||||
|
vec![RlvValue::Real(4.0)],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(
|
||||||
|
RlvRestrictionType::SitTp,
|
||||||
|
2,
|
||||||
|
vec![RlvValue::Real(2.0)],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
assert!(permissions.can_sit_tp(&mut distance).unwrap());
|
||||||
|
assert_eq!(distance, 2.0);
|
||||||
|
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(
|
||||||
|
RlvRestrictionType::RedirChat,
|
||||||
|
3,
|
||||||
|
vec![RlvValue::Integer(7)],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(
|
||||||
|
RlvRestrictionType::RedirChat,
|
||||||
|
4,
|
||||||
|
vec![RlvValue::Integer(7)],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
let mut channels = Vec::new();
|
||||||
|
assert!(permissions.try_get_redir_chat_channels(&mut channels));
|
||||||
|
assert_eq!(channels, [7]);
|
||||||
|
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(RlvRestrictionType::SendChannel, 5, vec![]))
|
||||||
|
.unwrap();
|
||||||
|
assert!(!permissions.can_chat(8, "private".into()).unwrap());
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(
|
||||||
|
RlvRestrictionType::SendChannel,
|
||||||
|
5,
|
||||||
|
vec![RlvValue::Integer(8)],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
assert!(permissions.can_chat(8, "private".into()).unwrap());
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(
|
||||||
|
RlvRestrictionType::SendChannelExcept,
|
||||||
|
6,
|
||||||
|
vec![RlvValue::Integer(8)],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
assert!(!permissions.can_chat(8, "private".into()).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn touch_and_hover_rules_apply_early_overrides_and_explicit_targets() {
|
||||||
|
let manager = RlvRestrictionManager::new();
|
||||||
|
let permissions = RlvPermissionsService::new(manager.clone());
|
||||||
|
let prim = guid(80);
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(RlvRestrictionType::TouchAll, 1, vec![]))
|
||||||
|
.unwrap();
|
||||||
|
assert!(
|
||||||
|
!permissions
|
||||||
|
.can_touch(
|
||||||
|
RlvPermissionsServiceTouchLocation::RezzedInWorld,
|
||||||
|
prim,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(RlvRestrictionType::TouchMe, 80, vec![]))
|
||||||
|
.unwrap();
|
||||||
|
assert!(
|
||||||
|
permissions
|
||||||
|
.can_touch(
|
||||||
|
RlvPermissionsServiceTouchLocation::RezzedInWorld,
|
||||||
|
prim,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(
|
||||||
|
RlvRestrictionType::FarTouch,
|
||||||
|
2,
|
||||||
|
vec![RlvValue::Real(1.5)],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
assert!(
|
||||||
|
!permissions
|
||||||
|
.can_touch(
|
||||||
|
RlvPermissionsServiceTouchLocation::RezzedInWorld,
|
||||||
|
prim,
|
||||||
|
None,
|
||||||
|
Some(Some(2.0)),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(
|
||||||
|
RlvRestrictionType::ShowHoverText,
|
||||||
|
3,
|
||||||
|
vec![RlvValue::Uuid(prim)],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
assert!(
|
||||||
|
!permissions
|
||||||
|
.can_show_hover_text(
|
||||||
|
RlvPermissionsServiceHoverTextLocation::World,
|
||||||
|
Some(Some(prim)),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
permissions
|
||||||
|
.can_show_hover_text(
|
||||||
|
RlvPermissionsServiceHoverTextLocation::World,
|
||||||
|
Some(Some(guid(81))),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn attach_and_detach_permissions_combine_item_type_source_and_folder_locks() {
|
||||||
|
let root = RlvSharedFolder::new(guid(1), "#RLV".into()).unwrap();
|
||||||
|
let folder = root.add_child(guid(2), "clothes".into()).unwrap();
|
||||||
|
let item = folder
|
||||||
|
.add_item(
|
||||||
|
guid(3),
|
||||||
|
"shirt".into(),
|
||||||
|
false,
|
||||||
|
Some(Some(RlvAttachmentPoint::Chest)),
|
||||||
|
Some(Some(guid(30))),
|
||||||
|
Some(Some(RlvWearableType::Shirt)),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let manager = RlvRestrictionManager::new();
|
||||||
|
manager.set_inventory_map(Some(InventoryMap::new(root, vec![]).unwrap()));
|
||||||
|
let permissions = RlvPermissionsService::new(manager.clone());
|
||||||
|
assert!(
|
||||||
|
permissions
|
||||||
|
.can_attach_with_rlv_inventory_item_boolean(item.clone(), true)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
permissions
|
||||||
|
.can_detach_with_rlv_inventory_item(item.clone())
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(RlvRestrictionType::DetachThis, 30, vec![]))
|
||||||
|
.unwrap();
|
||||||
|
assert!(
|
||||||
|
!permissions
|
||||||
|
.can_detach_with_rlv_inventory_item(item.clone())
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(
|
||||||
|
RlvRestrictionType::AttachThis,
|
||||||
|
31,
|
||||||
|
vec![RlvValue::String("clothes".into())],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
assert!(
|
||||||
|
!permissions
|
||||||
|
.can_attach_with_rlv_inventory_item_boolean(item.clone(), true)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
manager
|
||||||
|
.add_restriction(restriction(
|
||||||
|
RlvRestrictionType::RemOutfit,
|
||||||
|
32,
|
||||||
|
vec![RlvValue::WearableType(RlvWearableType::Shirt)],
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
assert!(
|
||||||
|
!permissions
|
||||||
|
.can_detach_with_rlv_inventory_item(item)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn recursive_folder_locks_and_exceptions_publish_isolated_snapshots() {
|
||||||
|
let root = RlvSharedFolder::new(guid(1), "#RLV".into()).unwrap();
|
||||||
|
let locked = root.add_child(guid(2), "locked".into()).unwrap();
|
||||||
|
let nested = locked.add_child(guid(3), "nested".into()).unwrap();
|
||||||
|
locked
|
||||||
|
.add_item(
|
||||||
|
guid(4),
|
||||||
|
"worn object".into(),
|
||||||
|
false,
|
||||||
|
Some(Some(RlvAttachmentPoint::Chest)),
|
||||||
|
Some(Some(guid(44))),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let inventory = InventoryMap::new(root, vec![]).unwrap();
|
||||||
|
let manager = RlvRestrictionManager::new();
|
||||||
|
manager.set_inventory_map(Some(inventory));
|
||||||
|
let lock = restriction(RlvRestrictionType::DetachAllThis, 44, vec![]);
|
||||||
|
manager.add_restriction(lock.clone()).unwrap();
|
||||||
|
|
||||||
|
let first_snapshot = manager.get_locked_folders().unwrap();
|
||||||
|
assert!(!first_snapshot[&locked.id()].can_detach());
|
||||||
|
assert!(!first_snapshot[&nested.id()].can_detach());
|
||||||
|
let exception = restriction(
|
||||||
|
RlvRestrictionType::DetachAllThisExcept,
|
||||||
|
45,
|
||||||
|
vec![RlvValue::String("locked".into())],
|
||||||
|
);
|
||||||
|
manager.add_restriction(exception).unwrap();
|
||||||
|
let second_snapshot = manager.get_locked_folders().unwrap();
|
||||||
|
assert!(second_snapshot[&locked.id()].can_detach());
|
||||||
|
assert!(second_snapshot[&nested.id()].can_detach());
|
||||||
|
// Previously returned snapshots remain immutable after later manager updates.
|
||||||
|
assert!(!first_snapshot[&locked.id()].can_detach());
|
||||||
|
assert!(manager.remove_restriction(&lock));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn external_inventory_dictionary_membership_is_snapshotted_with_reference_values() {
|
||||||
|
let root = RlvSharedFolder::new(guid(1), "#RLV".into()).unwrap();
|
||||||
|
let mut external = RlvInventoryItem::new(
|
||||||
|
guid(2),
|
||||||
|
"outside".into(),
|
||||||
|
false,
|
||||||
|
Some(Some(guid(3))),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let map = InventoryMap::new(root, vec![external.clone()]).unwrap();
|
||||||
|
external.set_name("changed".into());
|
||||||
|
// Items use C# reference semantics, while the dictionary's membership is a
|
||||||
|
// fixed immutable snapshot. Mutating the referenced item is visible.
|
||||||
|
assert_eq!(map.external_items().len(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
map.external_items().get(&guid(2)).unwrap().name(),
|
||||||
|
"changed"
|
||||||
|
);
|
||||||
|
external.set_attached_prim_id(Some(Some(guid(4))));
|
||||||
|
assert_eq!(
|
||||||
|
map.external_items()
|
||||||
|
.get(&guid(2))
|
||||||
|
.unwrap()
|
||||||
|
.attached_prim_id(),
|
||||||
|
Some(Some(guid(4)))
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1151,7 +1151,100 @@ impl<T> Future for AsyncEnumerableNext<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ImmutableDictionary<TKey, TValue>(pub PhantomData<fn(TKey, TValue)>);
|
/// Read-only, cheaply clonable hash-map snapshot corresponding to
|
||||||
|
/// `System.Collections.Immutable.ImmutableDictionary<TKey, TValue>`.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ImmutableDictionary<TKey, TValue>(Arc<std::collections::HashMap<TKey, TValue>>);
|
||||||
|
|
||||||
|
impl<TKey, TValue> PartialEq for ImmutableDictionary<TKey, TValue>
|
||||||
|
where
|
||||||
|
TKey: Eq + std::hash::Hash,
|
||||||
|
TValue: PartialEq,
|
||||||
|
{
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0 == other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TKey, TValue> Eq for ImmutableDictionary<TKey, TValue>
|
||||||
|
where
|
||||||
|
TKey: Eq + std::hash::Hash,
|
||||||
|
TValue: Eq,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TKey, TValue> ImmutableDictionary<TKey, TValue> {
|
||||||
|
/// Creates an immutable snapshot from an owned map.
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_map(values: std::collections::HashMap<TKey, TValue>) -> Self {
|
||||||
|
Self(Arc::new(values))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of entries in the snapshot.
|
||||||
|
#[must_use]
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the snapshot contains no entries.
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterates over keys and values in the snapshot.
|
||||||
|
#[must_use]
|
||||||
|
pub fn iter(&self) -> std::collections::hash_map::Iter<'_, TKey, TValue> {
|
||||||
|
self.0.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterates over values in the snapshot.
|
||||||
|
#[must_use]
|
||||||
|
pub fn values(&self) -> std::collections::hash_map::Values<'_, TKey, TValue> {
|
||||||
|
self.0.values()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Looks up a value by borrowed key.
|
||||||
|
#[must_use]
|
||||||
|
pub fn get<Q>(&self, key: &Q) -> Option<&TValue>
|
||||||
|
where
|
||||||
|
TKey: std::borrow::Borrow<Q> + Eq + std::hash::Hash,
|
||||||
|
Q: Eq + std::hash::Hash + ?Sized,
|
||||||
|
{
|
||||||
|
self.0.get(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TKey, TValue> Default for ImmutableDictionary<TKey, TValue> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(Arc::new(std::collections::HashMap::new()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TKey, TValue> From<std::collections::HashMap<TKey, TValue>>
|
||||||
|
for ImmutableDictionary<TKey, TValue>
|
||||||
|
{
|
||||||
|
fn from(values: std::collections::HashMap<TKey, TValue>) -> Self {
|
||||||
|
Self::from_map(values)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TKey, TValue> std::ops::Deref for ImmutableDictionary<TKey, TValue> {
|
||||||
|
type Target = std::collections::HashMap<TKey, TValue>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, TKey, TValue> IntoIterator for &'a ImmutableDictionary<TKey, TValue> {
|
||||||
|
type Item = (&'a TKey, &'a TValue);
|
||||||
|
type IntoIter = std::collections::hash_map::Iter<'a, TKey, TValue>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Read-only, cheaply clonable snapshot corresponding to
|
/// Read-only, cheaply clonable snapshot corresponding to
|
||||||
/// `System.Collections.Immutable.ImmutableList<T>`.
|
/// `System.Collections.Immutable.ImmutableList<T>`.
|
||||||
|
|||||||
123
tools/check_milestone_10_issue_79.py
Normal file
123
tools/check_milestone_10_issue_79.py
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Audit issue 79's native RLV state, inventory, lock, and permission boundary."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import generate_api_shims
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
SOURCE = ROOT / "crates" / "libremetaverse-rlv" / "src" / "state.rs"
|
||||||
|
GENERATED = ROOT / "crates" / "libremetaverse-rlv" / "src" / "generated.rs"
|
||||||
|
TESTS = ROOT / "crates" / "libremetaverse-rlv" / "tests" / "state_semantics.rs"
|
||||||
|
COMPAT = ROOT / "tests" / "compat" / "tests" / "rlv_inventory_map_semantics.rs"
|
||||||
|
DOC = ROOT / "crates" / "libremetaverse-rlv" / "README.md"
|
||||||
|
WORKFLOW = ROOT / ".gitea" / "workflows" / "rust-workspace.yml"
|
||||||
|
CATALOG = ROOT / "api" / "public-api.json"
|
||||||
|
STUB_RE = re.compile(r"\b(?:not_implemented|unimplemented_api)\b|\b(?:todo|unimplemented)!\s*\(")
|
||||||
|
|
||||||
|
TYPES = {
|
||||||
|
"T:LibreMetaverse.RLV.CameraRestrictions": "crate::state::CameraRestrictions",
|
||||||
|
"T:LibreMetaverse.RLV.CameraSettings": "crate::state::CameraSettings",
|
||||||
|
"T:LibreMetaverse.RLV.EventArguments.RestrictionUpdatedEventArgs":
|
||||||
|
"crate::state::RestrictionUpdatedEventArgs",
|
||||||
|
"T:LibreMetaverse.RLV.InventoryMap": "crate::state::InventoryMap",
|
||||||
|
"T:LibreMetaverse.RLV.LockedFolderPublic": "crate::state::LockedFolderPublic",
|
||||||
|
"T:LibreMetaverse.RLV.RlvBlacklist": "crate::state::RlvBlacklist",
|
||||||
|
"T:LibreMetaverse.RLV.RlvInventoryItem": "crate::state::RlvInventoryItem",
|
||||||
|
"T:LibreMetaverse.RLV.RlvPermissionsService": "crate::state::RlvPermissionsService",
|
||||||
|
"T:LibreMetaverse.RLV.RlvRestrictionManager": "crate::state::RlvRestrictionManager",
|
||||||
|
"T:LibreMetaverse.RLV.RlvSharedFolder": "crate::state::RlvSharedFolder",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def require_markers(path: Path, markers: tuple[str, ...]) -> None:
|
||||||
|
text = path.read_text()
|
||||||
|
missing = [marker for marker in markers if marker not in text]
|
||||||
|
if missing:
|
||||||
|
raise SystemExit(f"{path.name}: audit evidence missing: " + ", ".join(missing))
|
||||||
|
|
||||||
|
|
||||||
|
def catalog_members() -> set[str]:
|
||||||
|
catalog = json.loads(CATALOG.read_text())
|
||||||
|
assembly = next(
|
||||||
|
value for value in catalog["assemblies"]
|
||||||
|
if value["identity"]["name"] == "LibreMetaverse.RLV"
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
member["doc_id"]
|
||||||
|
for api_type in assembly["types"]
|
||||||
|
if api_type["doc_id"] in TYPES
|
||||||
|
for member in api_type["members"]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def generated_type_block(text: str, doc_id: str) -> str:
|
||||||
|
marker = f"/// C# type: `{doc_id}`."
|
||||||
|
start = text.find(marker)
|
||||||
|
if start < 0:
|
||||||
|
raise SystemExit(f"generated declaration is missing for {doc_id}")
|
||||||
|
next_type = text.find("/// C# type:", start + len(marker))
|
||||||
|
return text[start:] if next_type < 0 else text[start:next_type]
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
for api_type, declaration in TYPES.items():
|
||||||
|
if generate_api_shims.NATIVE_DECLARATIONS.get(api_type) != declaration:
|
||||||
|
raise SystemExit(f"issue 79 native declaration is missing for {api_type}")
|
||||||
|
members = catalog_members()
|
||||||
|
missing = sorted(members - set(generate_api_shims.NATIVE_MEMBER_BODIES))
|
||||||
|
if missing:
|
||||||
|
raise SystemExit("issue 79 native members missing: " + ", ".join(missing))
|
||||||
|
|
||||||
|
source = SOURCE.read_text()
|
||||||
|
if STUB_RE.search(source):
|
||||||
|
raise SystemExit("issue 79 owned Rust stubs remain in state.rs")
|
||||||
|
generated = GENERATED.read_text()
|
||||||
|
for api_type in TYPES:
|
||||||
|
if STUB_RE.search(generated_type_block(generated, api_type)):
|
||||||
|
raise SystemExit(f"issue 79 owned generated stubs remain for {api_type}")
|
||||||
|
|
||||||
|
require_markers(SOURCE, (
|
||||||
|
"MAX_RLV_INVENTORY_FOLDERS", "MAX_RLV_INVENTORY_ITEMS",
|
||||||
|
"MAX_RLV_RESTRICTIONS", "pub struct InventoryMap",
|
||||||
|
"pub struct RlvRestrictionManager", "pub struct RlvPermissionsService",
|
||||||
|
"pub struct CameraRestrictions", "pub struct LockedFolderPublic",
|
||||||
|
"pub struct RlvBlacklist", "fn build_locked_folders",
|
||||||
|
"fn secure_allowed", "fn rebuild_locked_folders", "fn emit",
|
||||||
|
"state.revision", "Arc<RwLock", "ImmutableDictionary", "CancellationToken",
|
||||||
|
))
|
||||||
|
require_markers(TESTS, (
|
||||||
|
"dispatches_after_unlocking", "concurrent_independent_sources",
|
||||||
|
"stale_revision", "counts_repeated_inputs_before_deduplication",
|
||||||
|
"blacklist_is_case_insensitive", "camera_restrictions_apply",
|
||||||
|
"constructor_field_order", "secure_rules_take_precedence",
|
||||||
|
"recursive_folder_locks", "snapshots remain immutable",
|
||||||
|
))
|
||||||
|
require_markers(COMPAT, (
|
||||||
|
"try_get_folder_when_name_contains_slash",
|
||||||
|
"try_get_folder_prefers_exact_slash_match",
|
||||||
|
"try_get_folder_skips_hidden_folder",
|
||||||
|
"find_folders_containing_by_prim_id",
|
||||||
|
"find_folders_containing_by_attachment_point_single_result",
|
||||||
|
"find_folders_containing_by_wearable_type",
|
||||||
|
))
|
||||||
|
require_markers(DOC, (
|
||||||
|
"pure state boundary", "immutable dictionary/list membership snapshots",
|
||||||
|
"after manager locks are released", "secure and explicit", "no network",
|
||||||
|
"ubuntu-latest",
|
||||||
|
))
|
||||||
|
require_markers(WORKFLOW, ("python3 tools/check_milestone_10_issue_79.py",))
|
||||||
|
print(
|
||||||
|
"issue 79 audit: native restriction state, inventory maps, recursive locks, "
|
||||||
|
"camera aggregation, blacklist, permission precedence, concurrency evidence, "
|
||||||
|
"compatibility tests, and docs are present"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -466,8 +466,18 @@ NATIVE_TYPES = {
|
|||||||
# below. This is used for static namespace types such as OSDParser: the type is
|
# below. This is used for static namespace types such as OSDParser: the type is
|
||||||
# hand-written, while its fixed public methods remain generator-audited.
|
# hand-written, while its fixed public methods remain generator-audited.
|
||||||
NATIVE_DECLARATIONS = {
|
NATIVE_DECLARATIONS = {
|
||||||
|
"T:LibreMetaverse.RLV.CameraRestrictions": "crate::state::CameraRestrictions",
|
||||||
|
"T:LibreMetaverse.RLV.CameraSettings": "crate::state::CameraSettings",
|
||||||
|
"T:LibreMetaverse.RLV.EventArguments.RestrictionUpdatedEventArgs": "crate::state::RestrictionUpdatedEventArgs",
|
||||||
|
"T:LibreMetaverse.RLV.InventoryMap": "crate::state::InventoryMap",
|
||||||
|
"T:LibreMetaverse.RLV.LockedFolderPublic": "crate::state::LockedFolderPublic",
|
||||||
|
"T:LibreMetaverse.RLV.RlvBlacklist": "crate::state::RlvBlacklist",
|
||||||
"T:LibreMetaverse.RLV.RlvCommon": "crate::protocol::RlvCommon",
|
"T:LibreMetaverse.RLV.RlvCommon": "crate::protocol::RlvCommon",
|
||||||
|
"T:LibreMetaverse.RLV.RlvInventoryItem": "crate::state::RlvInventoryItem",
|
||||||
|
"T:LibreMetaverse.RLV.RlvPermissionsService": "crate::state::RlvPermissionsService",
|
||||||
"T:LibreMetaverse.RLV.RlvRestriction": "crate::protocol::RlvRestriction",
|
"T:LibreMetaverse.RLV.RlvRestriction": "crate::protocol::RlvRestriction",
|
||||||
|
"T:LibreMetaverse.RLV.RlvRestrictionManager": "crate::state::RlvRestrictionManager",
|
||||||
|
"T:LibreMetaverse.RLV.RlvSharedFolder": "crate::state::RlvSharedFolder",
|
||||||
"T:LibreMetaverse.AgentManager": "crate::agent_manager::AgentManager",
|
"T:LibreMetaverse.AgentManager": "crate::agent_manager::AgentManager",
|
||||||
"T:LibreMetaverse.Animation": "crate::avatar_manager::Animation",
|
"T:LibreMetaverse.Animation": "crate::avatar_manager::Animation",
|
||||||
"T:LibreMetaverse.Animesh.AnimationTrack": "crate::animesh_runtime::AnimationTrack",
|
"T:LibreMetaverse.Animesh.AnimationTrack": "crate::animesh_runtime::AnimationTrack",
|
||||||
@@ -2427,6 +2437,194 @@ NATIVE_MEMBER_BODIES = {
|
|||||||
"crate::byte_order::write_double_little_endian(dest, pos, value)",
|
"crate::byte_order::write_double_little_endian(dest, pos, value)",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Native RLV state, inventory, lock, camera, and permission layer. Property
|
||||||
|
# setter entries use the generator's ``#set`` suffix so both C# accessors stay
|
||||||
|
# tied to explicit Rust implementations.
|
||||||
|
NATIVE_MEMBER_BODIES.update({
|
||||||
|
"P:LibreMetaverse.RLV.CameraRestrictions.AvDist": "self.native_av_dist()",
|
||||||
|
"P:LibreMetaverse.RLV.CameraRestrictions.AvDistMax": "self.native_av_dist_max()",
|
||||||
|
"P:LibreMetaverse.RLV.CameraRestrictions.AvDistMin": "self.native_av_dist_min()",
|
||||||
|
"P:LibreMetaverse.RLV.CameraRestrictions.DrawAlphaMax": "self.native_draw_alpha_max()",
|
||||||
|
"P:LibreMetaverse.RLV.CameraRestrictions.DrawAlphaMin": "self.native_draw_alpha_min()",
|
||||||
|
"P:LibreMetaverse.RLV.CameraRestrictions.DrawColor": "self.native_draw_color()",
|
||||||
|
"P:LibreMetaverse.RLV.CameraRestrictions.DrawMax": "self.native_draw_max()",
|
||||||
|
"P:LibreMetaverse.RLV.CameraRestrictions.DrawMin": "self.native_draw_min()",
|
||||||
|
"P:LibreMetaverse.RLV.CameraRestrictions.FovMax": "self.native_fov_max()",
|
||||||
|
"P:LibreMetaverse.RLV.CameraRestrictions.FovMin": "self.native_fov_min()",
|
||||||
|
"P:LibreMetaverse.RLV.CameraRestrictions.IsLocked": "self.native_is_locked()",
|
||||||
|
"P:LibreMetaverse.RLV.CameraRestrictions.Texture": "self.native_texture()",
|
||||||
|
"P:LibreMetaverse.RLV.CameraRestrictions.ZoomMax": "self.native_zoom_max()",
|
||||||
|
"P:LibreMetaverse.RLV.CameraRestrictions.ZoomMin": "self.native_zoom_min()",
|
||||||
|
"M:LibreMetaverse.RLV.CameraSettings.#ctor(System.Single,System.Single,System.Single,System.Single,System.Single,System.Single)":
|
||||||
|
"Self::native_new(av_dist_min, av_dist_max, fov_min, fov_max, zoom_min, current_fov)",
|
||||||
|
"P:LibreMetaverse.RLV.CameraSettings.AvDistMax": "self.native_av_dist_max()",
|
||||||
|
"P:LibreMetaverse.RLV.CameraSettings.AvDistMin": "self.native_av_dist_min()",
|
||||||
|
"P:LibreMetaverse.RLV.CameraSettings.CurrentFov": "self.native_current_fov()",
|
||||||
|
"P:LibreMetaverse.RLV.CameraSettings.FovMax": "self.native_fov_max()",
|
||||||
|
"P:LibreMetaverse.RLV.CameraSettings.FovMin": "self.native_fov_min()",
|
||||||
|
"P:LibreMetaverse.RLV.CameraSettings.ZoomMin": "self.native_zoom_min()",
|
||||||
|
"M:LibreMetaverse.RLV.EventArguments.RestrictionUpdatedEventArgs.#ctor(LibreMetaverse.RLV.RlvRestriction,System.Boolean,System.Boolean)":
|
||||||
|
"Self::native_new(restriction, is_new, is_deleted)",
|
||||||
|
"P:LibreMetaverse.RLV.EventArguments.RestrictionUpdatedEventArgs.IsDeleted": "self.native_is_deleted()",
|
||||||
|
"P:LibreMetaverse.RLV.EventArguments.RestrictionUpdatedEventArgs.IsNew": "self.native_is_new()",
|
||||||
|
"P:LibreMetaverse.RLV.EventArguments.RestrictionUpdatedEventArgs.Restriction": "self.native_restriction()",
|
||||||
|
"M:LibreMetaverse.RLV.InventoryMap.#ctor(LibreMetaverse.RLV.RlvSharedFolder,System.Collections.Generic.IReadOnlyCollection{LibreMetaverse.RLV.RlvInventoryItem})":
|
||||||
|
"Self::native_new(root, external_items)",
|
||||||
|
"M:LibreMetaverse.RLV.InventoryMap.FindFoldersContaining(System.Boolean,System.Nullable{System.Guid},System.Nullable{LibreMetaverse.RLV.RlvAttachmentPoint},System.Nullable{LibreMetaverse.RLV.RlvWearableType})":
|
||||||
|
"self.native_find_folders_containing(limit_to_one_result, attached_prim_id, attachment_point, wearable_type)",
|
||||||
|
"M:LibreMetaverse.RLV.InventoryMap.GetItemsByAttachmentPoint(LibreMetaverse.RLV.RlvAttachmentPoint)":
|
||||||
|
"self.native_get_items_by_attachment_point(attachment_point)",
|
||||||
|
"M:LibreMetaverse.RLV.InventoryMap.GetItemsById(System.Guid)": "self.native_get_items_by_id(item_id)",
|
||||||
|
"M:LibreMetaverse.RLV.InventoryMap.GetItemsByPrimId(System.Guid)": "self.native_get_items_by_prim_id(prim_id)",
|
||||||
|
"M:LibreMetaverse.RLV.InventoryMap.GetItemsByWearableType(LibreMetaverse.RLV.RlvWearableType)":
|
||||||
|
"self.native_get_items_by_wearable_type(wearable_type)",
|
||||||
|
"M:LibreMetaverse.RLV.InventoryMap.TryBuildPathToFolder(System.Guid,System.String@)":
|
||||||
|
"self.native_try_build_path_to_folder(folder_id, final_path)",
|
||||||
|
"M:LibreMetaverse.RLV.InventoryMap.TryGetFolderFromPath(System.String,System.Boolean,LibreMetaverse.RLV.RlvSharedFolder@)":
|
||||||
|
"self.native_try_get_folder_from_path(path, skip_private_folders, folder)",
|
||||||
|
"P:LibreMetaverse.RLV.InventoryMap.ExternalItems": "self.native_external_items()",
|
||||||
|
"P:LibreMetaverse.RLV.InventoryMap.Folders": "self.native_folders()",
|
||||||
|
"P:LibreMetaverse.RLV.InventoryMap.Items": "self.native_items()",
|
||||||
|
"P:LibreMetaverse.RLV.InventoryMap.Root": "self.native_root()",
|
||||||
|
"M:LibreMetaverse.RLV.LockedFolderPublic.ToString": "self.native_to_string()",
|
||||||
|
"P:LibreMetaverse.RLV.LockedFolderPublic.AttachExceptions": "self.native_attach_exceptions()",
|
||||||
|
"P:LibreMetaverse.RLV.LockedFolderPublic.AttachRestrictions": "self.native_attach_restrictions()",
|
||||||
|
"P:LibreMetaverse.RLV.LockedFolderPublic.CanAttach": "self.native_can_attach()",
|
||||||
|
"P:LibreMetaverse.RLV.LockedFolderPublic.CanDetach": "self.native_can_detach()",
|
||||||
|
"P:LibreMetaverse.RLV.LockedFolderPublic.DetachExceptions": "self.native_detach_exceptions()",
|
||||||
|
"P:LibreMetaverse.RLV.LockedFolderPublic.DetachRestrictions": "self.native_detach_restrictions()",
|
||||||
|
"P:LibreMetaverse.RLV.LockedFolderPublic.Id": "self.native_id()",
|
||||||
|
"P:LibreMetaverse.RLV.LockedFolderPublic.IsLocked": "self.native_is_locked()",
|
||||||
|
"P:LibreMetaverse.RLV.LockedFolderPublic.Name": "self.native_name()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvBlacklist.BlacklistBehavior(System.String)": "self.native_blacklist_behavior(behavior)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvBlacklist.GetBlacklist": "self.native_get_blacklist()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvBlacklist.IsBlacklisted(System.String)": "self.native_is_blacklisted(behavior)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvBlacklist.UnBlacklistBehavior(System.String)": "self.native_un_blacklist_behavior(behavior)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvInventoryItem.#ctor(System.Guid,System.String,System.Boolean,System.Nullable{System.Guid},System.Nullable{LibreMetaverse.RLV.RlvAttachmentPoint},System.Nullable{System.Guid},System.Nullable{LibreMetaverse.RLV.RlvWearableType},System.Nullable{LibreMetaverse.RLV.RlvGestureState})":
|
||||||
|
"Self::native_new(id, name, is_link, external_folder_id, attached_to, attached_prim_id, worn_on, gesture_state)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvInventoryItem.ToString": "self.native_to_string()",
|
||||||
|
"P:LibreMetaverse.RLV.RlvInventoryItem.AttachedPrimId": "self.native_attached_prim_id()",
|
||||||
|
"P:LibreMetaverse.RLV.RlvInventoryItem.AttachedPrimId#set": "self.native_set_attached_prim_id(value)",
|
||||||
|
"P:LibreMetaverse.RLV.RlvInventoryItem.AttachedTo": "self.native_attached_to()",
|
||||||
|
"P:LibreMetaverse.RLV.RlvInventoryItem.AttachedTo#set": "self.native_set_attached_to(value)",
|
||||||
|
"P:LibreMetaverse.RLV.RlvInventoryItem.Folder": "self.native_folder()",
|
||||||
|
"P:LibreMetaverse.RLV.RlvInventoryItem.FolderId": "self.native_folder_id()",
|
||||||
|
"P:LibreMetaverse.RLV.RlvInventoryItem.GestureState": "self.native_gesture_state()",
|
||||||
|
"P:LibreMetaverse.RLV.RlvInventoryItem.GestureState#set": "self.native_set_gesture_state(value)",
|
||||||
|
"P:LibreMetaverse.RLV.RlvInventoryItem.Id": "self.native_id()",
|
||||||
|
"P:LibreMetaverse.RLV.RlvInventoryItem.IsLink": "self.native_is_link()",
|
||||||
|
"P:LibreMetaverse.RLV.RlvInventoryItem.IsLink#set": "self.native_set_is_link(value)",
|
||||||
|
"P:LibreMetaverse.RLV.RlvInventoryItem.Name": "self.native_name()",
|
||||||
|
"P:LibreMetaverse.RLV.RlvInventoryItem.Name#set": "self.native_set_name(value)",
|
||||||
|
"P:LibreMetaverse.RLV.RlvInventoryItem.WornOn": "self.native_worn_on()",
|
||||||
|
"P:LibreMetaverse.RLV.RlvInventoryItem.WornOn#set": "self.native_set_worn_on(value)",
|
||||||
|
"E:LibreMetaverse.RLV.RlvRestrictionManager.RestrictionUpdated": "self.native_subscribe_restriction_updated(handler)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvRestrictionManager.FindRestrictions(System.String,System.Nullable{System.Guid})":
|
||||||
|
"self.native_find_restrictions(behavior_name_filter, sender_filter)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvRestrictionManager.GetLockedFolders": "self.native_get_locked_folders()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvRestrictionManager.GetRestrictionsByType(LibreMetaverse.RLV.RlvRestrictionType)":
|
||||||
|
"self.native_get_restrictions_by_type(restriction_type)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvRestrictionManager.GetTrackedPrimIds": "self.native_get_tracked_prim_ids()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvRestrictionManager.RemoveRestrictionsForObjectsAsync(System.Collections.Generic.IEnumerable{System.Guid},System.Threading.CancellationToken)":
|
||||||
|
"self.native_remove_restrictions_for_objects(prim_ids, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.RLV.RlvRestrictionManager.TryGetLockedFolder(System.Guid,LibreMetaverse.RLV.LockedFolderPublic@)":
|
||||||
|
"self.native_try_get_locked_folder(folder_id, locked_folder)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvSharedFolder.#ctor(System.Guid,System.String)": "Self::native_new(id, name)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvSharedFolder.AddChild(System.Guid,System.String)": "self.native_add_child(id, name)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvSharedFolder.AddItem(System.Guid,System.String,System.Boolean,System.Nullable{LibreMetaverse.RLV.RlvAttachmentPoint},System.Nullable{System.Guid},System.Nullable{LibreMetaverse.RLV.RlvWearableType},System.Nullable{LibreMetaverse.RLV.RlvGestureState})":
|
||||||
|
"self.native_add_item(id, name, is_link, attached_to, attached_prim_id, worn_on, gesture_state)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvSharedFolder.ToString": "self.native_to_string()",
|
||||||
|
"P:LibreMetaverse.RLV.RlvSharedFolder.Children": "self.native_children()",
|
||||||
|
"P:LibreMetaverse.RLV.RlvSharedFolder.Id": "self.native_id()",
|
||||||
|
"P:LibreMetaverse.RLV.RlvSharedFolder.Items": "self.native_items()",
|
||||||
|
"P:LibreMetaverse.RLV.RlvSharedFolder.Name": "self.native_name()",
|
||||||
|
"P:LibreMetaverse.RLV.RlvSharedFolder.Name#set": "self.native_set_name(value)",
|
||||||
|
"P:LibreMetaverse.RLV.RlvSharedFolder.Parent": "self.native_parent()",
|
||||||
|
"P:LibreMetaverse.RLV.RlvSharedFolder.Parent#set": "self.native_set_parent(value)",
|
||||||
|
})
|
||||||
|
|
||||||
|
NATIVE_MEMBER_BODIES.update({
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanAllowIdle": "self.native_can_allow_idle()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanAlwaysRun": "self.native_can_always_run()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanAttach(LibreMetaverse.RLV.RlvInventoryItem,System.Boolean)":
|
||||||
|
"self.native_can_attach_with_item(item, is_shared)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanAttach(System.Nullable{System.Guid},System.Boolean,System.Nullable{LibreMetaverse.RLV.RlvAttachmentPoint},System.Nullable{LibreMetaverse.RLV.RlvWearableType})":
|
||||||
|
"self.native_can_attach(object_folder_id, is_shared, attachment_point, wearable_type)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanChat(System.Int32,System.String)":
|
||||||
|
"self.native_can_chat(channel, message)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanChatNormal": "self.native_can_chat_normal()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanChatShout": "self.native_can_chat_shout()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanChatWhisper": "self.native_can_chat_whisper()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanDefaultWear": "self.native_can_default_wear()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanDetach(LibreMetaverse.RLV.RlvInventoryItem)":
|
||||||
|
"self.native_can_detach_with_item(item)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanDetach(System.Nullable{System.Guid},System.Nullable{System.Guid},System.Nullable{System.Guid},System.Boolean,System.Nullable{LibreMetaverse.RLV.RlvAttachmentPoint},System.Nullable{LibreMetaverse.RLV.RlvWearableType})":
|
||||||
|
"self.native_can_detach(item_id, prim_id, folder_id, is_shared, attachment_point, wearable_type)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanEdit(LibreMetaverse.RLV.RlvPermissionsService.ObjectLocation,System.Nullable{System.Guid})":
|
||||||
|
"self.native_can_edit(object_location, object_id)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanEmote": "self.native_can_emote()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanFly": "self.native_can_fly()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanInteract": "self.native_can_interact()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanJump": "self.native_can_jump()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanReceiveChat(System.String,System.Guid)":
|
||||||
|
"self.native_can_receive_chat(message, user_id)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanReceiveIM(System.String,System.Nullable{System.Guid},System.String)":
|
||||||
|
"self.native_can_receive_im(message, user_id, group_name)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanRez": "self.native_can_rez()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanSendChat": "self.native_can_send_chat()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanSendGesture": "self.native_can_send_gesture()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanSendIM(System.String,System.Nullable{System.Guid},System.String)":
|
||||||
|
"self.native_can_send_im(message, user_id, group_name)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanSetDebug": "self.native_can_set_debug()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanSetEnv": "self.native_can_set_env()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanSetGroup": "self.native_can_set_group()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanShare(System.Nullable{System.Guid})":
|
||||||
|
"self.native_can_share(user_id)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanShowHoverText(LibreMetaverse.RLV.RlvPermissionsService.HoverTextLocation,System.Nullable{System.Guid})":
|
||||||
|
"self.native_can_show_hover_text(location, object_id)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanShowInv": "self.native_can_show_inv()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanShowLoc": "self.native_can_show_loc()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanShowMiniMap": "self.native_can_show_mini_map()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanShowNameTags(System.Nullable{System.Guid})":
|
||||||
|
"self.native_can_show_name_tags(user_id)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanShowNames(System.Nullable{System.Guid})":
|
||||||
|
"self.native_can_show_names(user_id)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanShowNearby": "self.native_can_show_nearby()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanShowWorldMap": "self.native_can_show_world_map()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanSit": "self.native_can_sit()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanSitTp(System.Single@)": "self.native_can_sit_tp(max_distance)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanStandTp": "self.native_can_stand_tp()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanStartIM(System.Guid)": "self.native_can_start_im(user_id)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanTPLure(System.Nullable{System.Guid})":
|
||||||
|
"self.native_can_tp_lure(user_id)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanTempRun": "self.native_can_temp_run()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanTouch(LibreMetaverse.RLV.RlvPermissionsService.TouchLocation,System.Guid,System.Nullable{System.Guid},System.Nullable{System.Single})":
|
||||||
|
"self.native_can_touch(location, prim_id, user_id, distance)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanTpLm": "self.native_can_tp_lm()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanTpLoc": "self.native_can_tp_loc()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanTpLocal(System.Single@)": "self.native_can_tp_local(max_distance)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanTpRequest(System.Nullable{System.Guid})":
|
||||||
|
"self.native_can_tp_request(user_id)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanUnsit": "self.native_can_unsit()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanViewNote": "self.native_can_view_note()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanViewScript": "self.native_can_view_script()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.CanViewTexture": "self.native_can_view_texture()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.GetCameraRestrictions": "self.native_get_camera_restrictions()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.IsAutoAcceptPermissions": "self.native_is_auto_accept_permissions()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.IsAutoAcceptTp(System.Nullable{System.Guid})":
|
||||||
|
"self.native_is_auto_accept_tp(user_id)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.IsAutoAcceptTpRequest(System.Nullable{System.Guid})":
|
||||||
|
"self.native_is_auto_accept_tp_request(user_id)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.IsAutoDenyPermissions": "self.native_is_auto_deny_permissions()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.IsPermissive": "self.native_is_permissive()",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.TryGetMaxFarTouchDistance(System.Single@)":
|
||||||
|
"self.native_try_get_max_far_touch_distance(max_distance)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.TryGetRedirChatChannels(System.Collections.Generic.IReadOnlyList{System.Int32}@)":
|
||||||
|
"self.native_try_get_redir_chat_channels(channels)",
|
||||||
|
"M:LibreMetaverse.RLV.RlvPermissionsService.TryGetRedirEmoteChannels(System.Collections.Generic.IReadOnlyList{System.Int32}@)":
|
||||||
|
"self.native_try_get_redir_emote_channels(channels)",
|
||||||
|
})
|
||||||
|
|
||||||
CLIENT_CORE_NATIVE_OWNERS = (
|
CLIENT_CORE_NATIVE_OWNERS = (
|
||||||
"AgentSettings",
|
"AgentSettings",
|
||||||
"AssetCacheSettings",
|
"AssetCacheSettings",
|
||||||
|
|||||||
Reference in New Issue
Block a user