Implement native AgentManager services (#59)
This commit is contained in:
@@ -13,6 +13,7 @@ on:
|
|||||||
- "tools/check_api_coverage.py"
|
- "tools/check_api_coverage.py"
|
||||||
- "tools/check_codegen_gate.py"
|
- "tools/check_codegen_gate.py"
|
||||||
- "tools/check_milestone_08.py"
|
- "tools/check_milestone_08.py"
|
||||||
|
- "tools/check_milestone_09_issue_59.py"
|
||||||
- "crates/libremetaverse/Cargo.toml"
|
- "crates/libremetaverse/Cargo.toml"
|
||||||
- "crates/libremetaverse/src/generated.rs"
|
- "crates/libremetaverse/src/generated.rs"
|
||||||
- "crates/libremetaverse/src/packets.rs"
|
- "crates/libremetaverse/src/packets.rs"
|
||||||
@@ -37,6 +38,7 @@ on:
|
|||||||
- "tools/check_api_coverage.py"
|
- "tools/check_api_coverage.py"
|
||||||
- "tools/check_codegen_gate.py"
|
- "tools/check_codegen_gate.py"
|
||||||
- "tools/check_milestone_08.py"
|
- "tools/check_milestone_08.py"
|
||||||
|
- "tools/check_milestone_09_issue_59.py"
|
||||||
- "crates/libremetaverse/Cargo.toml"
|
- "crates/libremetaverse/Cargo.toml"
|
||||||
- "crates/libremetaverse/src/generated.rs"
|
- "crates/libremetaverse/src/generated.rs"
|
||||||
- "crates/libremetaverse/src/packets.rs"
|
- "crates/libremetaverse/src/packets.rs"
|
||||||
@@ -69,6 +71,7 @@ jobs:
|
|||||||
python3 tools/generate_api_shims.py --check
|
python3 tools/generate_api_shims.py --check
|
||||||
python3 tools/check_api_coverage.py
|
python3 tools/check_api_coverage.py
|
||||||
python3 tools/check_milestone_08.py
|
python3 tools/check_milestone_08.py
|
||||||
|
python3 tools/check_milestone_09_issue_59.py
|
||||||
cargo check --manifest-path tests/api-compile/Cargo.toml --locked
|
cargo check --manifest-path tests/api-compile/Cargo.toml --locked
|
||||||
- name: Test generated wire codecs and translated parity cases
|
- name: Test generated wire codecs and translated parity cases
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -25,5 +25,7 @@ jobs:
|
|||||||
components: rustfmt
|
components: rustfmt
|
||||||
- name: Check formatting
|
- name: Check formatting
|
||||||
run: cargo fmt --all -- --check
|
run: cargo fmt --all -- --check
|
||||||
|
- name: Audit native AgentManager boundary
|
||||||
|
run: python3 tools/check_milestone_09_issue_59.py
|
||||||
- name: Compile every workspace target with bounded memory
|
- name: Compile every workspace target with bounded memory
|
||||||
run: cargo check --workspace --all-targets --locked -j 1
|
run: cargo check --workspace --all-targets --locked -j 1
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -324,3 +324,18 @@ counters and receives real UDP and server-stat updates without exposing packet
|
|||||||
payloads or endpoints. The gate asserts token redaction, bounded policies, and
|
payloads or endpoints. The gate asserts token redaction, bounded policies, and
|
||||||
joined CAPS/download/network tasks; `tools/check_milestone_08.py` prevents owned
|
joined CAPS/download/network tasks; `tools/check_milestone_08.py` prevents owned
|
||||||
networking stubs or required audit evidence from regressing.
|
networking stubs or required audit evidence from regressing.
|
||||||
|
|
||||||
|
### Milestone 09
|
||||||
|
|
||||||
|
The first world-services slice provides a native non-movement `AgentManager`
|
||||||
|
over the milestone 08 transport. Chat, instant messages and conferences,
|
||||||
|
gestures, animation, viewer effects, balance and payments, profiles, notes,
|
||||||
|
picks, classifieds, display names, language/access/preferences, experiences,
|
||||||
|
viewer reporting, benefits, attachment resources, mute lists, and nav-mesh
|
||||||
|
status now use their C#-compatible LLUDP or capability paths. Incoming state is
|
||||||
|
updated before callbacks, and callbacks run outside synchronization locks.
|
||||||
|
Gesture assets have a native version-2 parser and executor with bounded HTTP
|
||||||
|
and UDP download paths. An offline fake grid verifies capability discovery and
|
||||||
|
the resulting chat, animation, and sound packets. The ownership, cancellation,
|
||||||
|
limits, and compatibility rules are documented in
|
||||||
|
[`docs/agent-manager.md`](docs/agent-manager.md).
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Generated by `python3 tools/generate_api_shims.py`; do not edit by hand.
|
|||||||
|
|
||||||
| Assembly | Types | Members | Status |
|
| Assembly | Types | Members | Status |
|
||||||
|---|---:|---:|---|
|
|---|---:|---:|---|
|
||||||
| `LibreMetaverse` | 2,711 | 27,281 | native implementation: 79 types / 14,127 members; remaining surface is callable failure-only shims |
|
| `LibreMetaverse` | 2,711 | 27,281 | native implementation: 121 types / 14,470 members; remaining surface is callable failure-only shims |
|
||||||
| `LibreMetaverse.Imaging.Abstractions` | 3 | 20 | native implementation: 3 types / 20 members; no generated shims remain |
|
| `LibreMetaverse.Imaging.Abstractions` | 3 | 20 | native implementation: 3 types / 20 members; no generated shims remain |
|
||||||
| `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 |
|
||||||
|
|||||||
4704
crates/libremetaverse/src/agent_manager.rs
Normal file
4704
crates/libremetaverse/src/agent_manager.rs
Normal file
File diff suppressed because it is too large
Load Diff
1213
crates/libremetaverse/src/agent_messages.rs
Normal file
1213
crates/libremetaverse/src/agent_messages.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -256,6 +256,12 @@ impl Caps {
|
|||||||
Ok(read(&self.inner.caps).get(&capability).cloned())
|
Ok(read(&self.inner.caps).get(&capability).cloned())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn seed_request_finished(&self) -> bool {
|
||||||
|
mutex(&self.inner.seed_task)
|
||||||
|
.as_ref()
|
||||||
|
.is_none_or(|task| task.handle.is_finished())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn disconnect(&self, immediate: bool) -> Result<(), Error> {
|
pub fn disconnect(&self, immediate: bool) -> Result<(), Error> {
|
||||||
self.inner.disconnect(immediate);
|
self.inner.disconnect(immediate);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ struct ClientRuntime {
|
|||||||
http_caps_client: Mutex<crate::caps_http::HttpCapsClient>,
|
http_caps_client: Mutex<crate::caps_http::HttpCapsClient>,
|
||||||
caps_rate_limiter: Mutex<crate::caps_http::CapsRateLimiter>,
|
caps_rate_limiter: Mutex<crate::caps_http::CapsRateLimiter>,
|
||||||
network_manager: Mutex<std::sync::Weak<crate::network_manager::NetworkManagerInner>>,
|
network_manager: Mutex<std::sync::Weak<crate::network_manager::NetworkManagerInner>>,
|
||||||
|
agent_manager: Mutex<std::sync::Weak<crate::agent_manager::AgentManagerInner>>,
|
||||||
shutdown_complete: Condvar,
|
shutdown_complete: Condvar,
|
||||||
shutdown_wait: Mutex<()>,
|
shutdown_wait: Mutex<()>,
|
||||||
}
|
}
|
||||||
@@ -144,6 +145,7 @@ impl ClientRuntime {
|
|||||||
http_caps_client: Mutex::new(http_caps_client),
|
http_caps_client: Mutex::new(http_caps_client),
|
||||||
caps_rate_limiter: Mutex::new(caps_rate_limiter),
|
caps_rate_limiter: Mutex::new(caps_rate_limiter),
|
||||||
network_manager: Mutex::new(std::sync::Weak::new()),
|
network_manager: Mutex::new(std::sync::Weak::new()),
|
||||||
|
agent_manager: Mutex::new(std::sync::Weak::new()),
|
||||||
shutdown_complete: Condvar::new(),
|
shutdown_complete: Condvar::new(),
|
||||||
shutdown_wait: Mutex::new(()),
|
shutdown_wait: Mutex::new(()),
|
||||||
}
|
}
|
||||||
@@ -242,11 +244,24 @@ impl ClientRuntime {
|
|||||||
/// Construction is side-effect free: it creates no runtime, task, socket, or
|
/// Construction is side-effect free: it creates no runtime, task, socket, or
|
||||||
/// HTTP request. Network-facing services are attached explicitly and are owned
|
/// HTTP request. Network-facing services are attached explicitly and are owned
|
||||||
/// until ordered shutdown or drop.
|
/// until ordered shutdown or drop.
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct GridClient {
|
pub struct GridClient {
|
||||||
pub(crate) settings: Settings,
|
pub(crate) settings: Settings,
|
||||||
pub(crate) time_provider: TimeProvider,
|
pub(crate) time_provider: TimeProvider,
|
||||||
runtime: Arc<ClientRuntime>,
|
runtime: Arc<ClientRuntime>,
|
||||||
|
network_manager: Option<crate::NetworkManager>,
|
||||||
|
agent_manager: Option<Box<crate::agent_manager::AgentManager>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for GridClient {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
settings: self.settings.clone(),
|
||||||
|
time_provider: self.time_provider.clone(),
|
||||||
|
runtime: Arc::clone(&self.runtime),
|
||||||
|
network_manager: self.network_manager.clone(),
|
||||||
|
agent_manager: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -353,6 +368,9 @@ impl GridClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn native_network(&self) -> Result<crate::NetworkManager, crate::Error> {
|
pub(crate) fn native_network(&self) -> Result<crate::NetworkManager, crate::Error> {
|
||||||
|
if let Some(manager) = &self.network_manager {
|
||||||
|
return Ok(manager.clone());
|
||||||
|
}
|
||||||
let mut cached = self
|
let mut cached = self
|
||||||
.runtime
|
.runtime
|
||||||
.network_manager
|
.network_manager
|
||||||
@@ -369,12 +387,44 @@ impl GridClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::needless_pass_by_value)] // The mapped C# property setter owns its value.
|
#[allow(clippy::needless_pass_by_value)] // The mapped C# property setter owns its value.
|
||||||
pub(crate) fn native_set_network(&self, value: crate::NetworkManager) {
|
pub(crate) fn native_set_network(&mut self, value: crate::NetworkManager) {
|
||||||
*self
|
*self
|
||||||
.runtime
|
.runtime
|
||||||
.network_manager
|
.network_manager
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap_or_else(std::sync::PoisonError::into_inner) = value.native_inner_weak();
|
.unwrap_or_else(std::sync::PoisonError::into_inner) = value.native_inner_weak();
|
||||||
|
self.network_manager = Some(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn native_self(&mut self) -> &mut crate::agent_manager::AgentManager {
|
||||||
|
if self.agent_manager.is_none() {
|
||||||
|
let client = Arc::new(self.clone());
|
||||||
|
let manager = crate::agent_manager::AgentManager::native_new(Some(client))
|
||||||
|
.unwrap_or_else(|_| panic!("failed to construct AgentManager"));
|
||||||
|
self.agent_manager = Some(Box::new(manager));
|
||||||
|
}
|
||||||
|
self.agent_manager.as_deref_mut().expect("agent manager")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn cached_agent_manager_inner(
|
||||||
|
&self,
|
||||||
|
) -> Option<Arc<crate::agent_manager::AgentManagerInner>> {
|
||||||
|
self.runtime
|
||||||
|
.agent_manager
|
||||||
|
.lock()
|
||||||
|
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||||
|
.upgrade()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn cache_agent_manager_inner(
|
||||||
|
&self,
|
||||||
|
inner: &Arc<crate::agent_manager::AgentManagerInner>,
|
||||||
|
) {
|
||||||
|
*self
|
||||||
|
.runtime
|
||||||
|
.agent_manager
|
||||||
|
.lock()
|
||||||
|
.unwrap_or_else(std::sync::PoisonError::into_inner) = Arc::downgrade(inner);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn native_set_caps_rate_limiter(
|
pub(crate) fn native_set_caps_rate_limiter(
|
||||||
@@ -416,7 +466,7 @@ impl fmt::Debug for GridClient {
|
|||||||
.field("lifecycle_state", &self.lifecycle_state())
|
.field("lifecycle_state", &self.lifecycle_state())
|
||||||
.field("services", &service_names)
|
.field("services", &service_names)
|
||||||
.field("time_provider", &self.time_provider)
|
.field("time_provider", &self.time_provider)
|
||||||
.finish()
|
.finish_non_exhaustive()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,6 +577,8 @@ impl GridClientBuilder {
|
|||||||
http_caps_client,
|
http_caps_client,
|
||||||
caps_rate_limiter,
|
caps_rate_limiter,
|
||||||
)),
|
)),
|
||||||
|
network_manager: None,
|
||||||
|
agent_manager: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
375
crates/libremetaverse/src/gesture.rs
Normal file
375
crates/libremetaverse/src/gesture.rs
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
//! Native gesture asset parsing and step types.
|
||||||
|
|
||||||
|
#![allow(clippy::inherent_to_string)] // Public names mirror C# ToString mappings.
|
||||||
|
#![allow(clippy::missing_errors_doc)] // Result shapes are fixed by the compatibility API.
|
||||||
|
|
||||||
|
use crate::{Error, assets::GestureStepType};
|
||||||
|
use libremetaverse_types::{AssetType, UUID};
|
||||||
|
use std::fmt::Write as _;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum GestureStep {
|
||||||
|
Animation(GestureStepAnimation),
|
||||||
|
Sound(GestureStepSound),
|
||||||
|
Chat(GestureStepChat),
|
||||||
|
Wait(GestureStepWait),
|
||||||
|
EOF(GestureStepEOF),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GestureStep {
|
||||||
|
#[must_use]
|
||||||
|
pub const fn gesture_step_type(&self) -> GestureStepType {
|
||||||
|
match self {
|
||||||
|
Self::Animation(_) => GestureStepType::Animation,
|
||||||
|
Self::Sound(_) => GestureStepType::Sound,
|
||||||
|
Self::Chat(_) => GestureStepType::Chat,
|
||||||
|
Self::Wait(_) => GestureStepType::Wait,
|
||||||
|
Self::EOF(_) => GestureStepType::EOF,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||||
|
pub struct GestureStepAnimation {
|
||||||
|
pub animation_start: bool,
|
||||||
|
pub id: UUID,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GestureStepAnimation {
|
||||||
|
pub fn new() -> Result<Self, Error> {
|
||||||
|
Ok(Self {
|
||||||
|
animation_start: true,
|
||||||
|
..Self::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
format!(
|
||||||
|
"{} animation: {}",
|
||||||
|
if self.animation_start {
|
||||||
|
"Start"
|
||||||
|
} else {
|
||||||
|
"Stop"
|
||||||
|
},
|
||||||
|
self.name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn gesture_step_type(&self) -> GestureStepType {
|
||||||
|
GestureStepType::Animation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||||
|
pub struct GestureStepSound {
|
||||||
|
pub id: UUID,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GestureStepSound {
|
||||||
|
pub fn new() -> Result<Self, Error> {
|
||||||
|
Ok(Self::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
format!("Sound: {}", self.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn gesture_step_type(&self) -> GestureStepType {
|
||||||
|
GestureStepType::Sound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||||
|
pub struct GestureStepChat {
|
||||||
|
pub text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GestureStepChat {
|
||||||
|
pub fn new() -> Result<Self, Error> {
|
||||||
|
Ok(Self::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
format!("Chat: {}", self.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn gesture_step_type(&self) -> GestureStepType {
|
||||||
|
GestureStepType::Chat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
pub struct GestureStepWait {
|
||||||
|
pub wait_for_animation: bool,
|
||||||
|
pub wait_for_time: bool,
|
||||||
|
pub wait_time: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GestureStepWait {
|
||||||
|
pub const fn new() -> Result<Self, Error> {
|
||||||
|
Ok(Self {
|
||||||
|
wait_for_animation: false,
|
||||||
|
wait_for_time: false,
|
||||||
|
wait_time: 0.0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
let mut result = String::from("-- Wait for: ");
|
||||||
|
if self.wait_for_animation {
|
||||||
|
result.push_str("(animations to finish) ");
|
||||||
|
}
|
||||||
|
if self.wait_for_time {
|
||||||
|
let _ = write!(result, "(time {:.1}s)", self.wait_time);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn gesture_step_type(&self) -> GestureStepType {
|
||||||
|
GestureStepType::Wait
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||||
|
pub struct GestureStepEOF;
|
||||||
|
|
||||||
|
impl GestureStepEOF {
|
||||||
|
pub const fn new() -> Result<Self, Error> {
|
||||||
|
Ok(Self)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
"End of gesture sequence".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn gesture_step_type(&self) -> GestureStepType {
|
||||||
|
GestureStepType::EOF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
|
pub struct AssetGesture {
|
||||||
|
pub replace_with: String,
|
||||||
|
pub sequence: Vec<GestureStep>,
|
||||||
|
pub trigger: String,
|
||||||
|
pub trigger_key: u8,
|
||||||
|
pub trigger_key_mask: u32,
|
||||||
|
asset_id: UUID,
|
||||||
|
asset_data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AssetGesture {
|
||||||
|
pub fn new_with_constructor() -> Result<Self, Error> {
|
||||||
|
Ok(Self::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_uuid_bytes(asset_id: UUID, asset_data: Vec<u8>) -> Result<Self, Error> {
|
||||||
|
Ok(Self {
|
||||||
|
asset_id,
|
||||||
|
asset_data,
|
||||||
|
..Self::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn asset_type(&self) -> AssetType {
|
||||||
|
AssetType::Gesture
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn asset_id(&self) -> UUID {
|
||||||
|
self.asset_id
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn asset_data(&self) -> &[u8] {
|
||||||
|
&self.asset_data
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode(&mut self) -> Result<bool, Error> {
|
||||||
|
Ok(self.decode_inner().is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_inner(&mut self) -> Result<(), Error> {
|
||||||
|
let text = String::from_utf8(self.asset_data.clone()).map_err(|_| Error::Argument)?;
|
||||||
|
let mut lines = text.split('\n');
|
||||||
|
if parse_line::<i32>(&mut lines)? != 2 {
|
||||||
|
return Err(Error::Argument);
|
||||||
|
}
|
||||||
|
self.trigger_key = parse_line(&mut lines)?;
|
||||||
|
self.trigger_key_mask = parse_line(&mut lines)?;
|
||||||
|
next_line(&mut lines)?.clone_into(&mut self.trigger);
|
||||||
|
next_line(&mut lines)?.clone_into(&mut self.replace_with);
|
||||||
|
let count = parse_line::<i32>(&mut lines)?;
|
||||||
|
if count < 0 {
|
||||||
|
return Err(Error::Argument);
|
||||||
|
}
|
||||||
|
let mut sequence = Vec::with_capacity(usize::try_from(count).map_err(|_| Error::Argument)?);
|
||||||
|
for _ in 0..count {
|
||||||
|
match parse_line::<i32>(&mut lines)? {
|
||||||
|
0 => {
|
||||||
|
let name = next_line(&mut lines)?.to_owned();
|
||||||
|
let id = UUID::new_with_string(
|
||||||
|
next_line(&mut lines)?.trim_end_matches('\r').into(),
|
||||||
|
)?;
|
||||||
|
let flags = parse_line::<i32>(&mut lines)?;
|
||||||
|
sequence.push(GestureStep::Animation(GestureStepAnimation {
|
||||||
|
animation_start: flags == 0,
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
let name = next_line(&mut lines)?.replace('\r', "");
|
||||||
|
let id = UUID::new_with_string(
|
||||||
|
next_line(&mut lines)?.trim_end_matches('\r').into(),
|
||||||
|
)?;
|
||||||
|
let _flags = parse_line::<i32>(&mut lines)?;
|
||||||
|
sequence.push(GestureStep::Sound(GestureStepSound { id, name }));
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
let text = next_line(&mut lines)?.to_owned();
|
||||||
|
let _flags = parse_line::<i32>(&mut lines)?;
|
||||||
|
sequence.push(GestureStep::Chat(GestureStepChat { text }));
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
let wait_time = parse_line::<f32>(&mut lines)?;
|
||||||
|
let flags = parse_line::<i32>(&mut lines)?;
|
||||||
|
sequence.push(GestureStep::Wait(GestureStepWait {
|
||||||
|
wait_for_animation: flags & 0x02 != 0,
|
||||||
|
wait_for_time: flags & 0x01 != 0,
|
||||||
|
wait_time,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
4 => {
|
||||||
|
sequence.push(GestureStep::EOF(GestureStepEOF));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// The reference ignores unknown step discriminants.
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.sequence = sequence;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(&mut self) -> Result<(), Error> {
|
||||||
|
let mut text = format!(
|
||||||
|
"2\n{}\n{}\n{}\n{}\n{}\n",
|
||||||
|
self.trigger_key,
|
||||||
|
self.trigger_key_mask,
|
||||||
|
self.trigger,
|
||||||
|
self.replace_with,
|
||||||
|
self.sequence.len()
|
||||||
|
);
|
||||||
|
for step in &self.sequence {
|
||||||
|
match step {
|
||||||
|
GestureStep::Animation(step) => {
|
||||||
|
let _ = write!(
|
||||||
|
text,
|
||||||
|
"0\n{}\n{}\n{}\n",
|
||||||
|
step.name,
|
||||||
|
step.id,
|
||||||
|
i32::from(!step.animation_start)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
GestureStep::Sound(step) => {
|
||||||
|
let _ = write!(text, "1\n{}\n{}\n0\n", step.name, step.id);
|
||||||
|
}
|
||||||
|
GestureStep::Chat(step) => {
|
||||||
|
let _ = write!(text, "2\n{}\n0\n", step.text);
|
||||||
|
}
|
||||||
|
GestureStep::Wait(step) => {
|
||||||
|
let flags =
|
||||||
|
i32::from(step.wait_for_time) | (i32::from(step.wait_for_animation) << 1);
|
||||||
|
let _ = write!(text, "3\n{:.6}\n{}\n", step.wait_time, flags);
|
||||||
|
}
|
||||||
|
GestureStep::EOF(_) => {
|
||||||
|
text.push_str("4\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.asset_data = text.into_bytes();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_line<'a>(lines: &mut impl Iterator<Item = &'a str>) -> Result<&'a str, Error> {
|
||||||
|
lines.next().ok_or(Error::Argument)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_line<'a, T: std::str::FromStr>(
|
||||||
|
lines: &mut impl Iterator<Item = &'a str>,
|
||||||
|
) -> Result<T, Error> {
|
||||||
|
next_line(lines)?
|
||||||
|
.trim_end_matches('\r')
|
||||||
|
.parse()
|
||||||
|
.map_err(|_| Error::Argument)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gesture_v2_roundtrip_preserves_all_step_payloads() {
|
||||||
|
let animation = UUID::random().unwrap();
|
||||||
|
let sound = UUID::random().unwrap();
|
||||||
|
let mut gesture = AssetGesture::new_with_constructor().unwrap();
|
||||||
|
gesture.trigger_key = 7;
|
||||||
|
gesture.trigger_key_mask = 3;
|
||||||
|
gesture.trigger = "/wave".into();
|
||||||
|
gesture.replace_with = "waves".into();
|
||||||
|
gesture.sequence = vec![
|
||||||
|
GestureStep::Chat(GestureStepChat {
|
||||||
|
text: "/5 hello".into(),
|
||||||
|
}),
|
||||||
|
GestureStep::Animation(GestureStepAnimation {
|
||||||
|
animation_start: true,
|
||||||
|
id: animation,
|
||||||
|
name: "Wave".into(),
|
||||||
|
}),
|
||||||
|
GestureStep::Sound(GestureStepSound {
|
||||||
|
id: sound,
|
||||||
|
name: "Bell".into(),
|
||||||
|
}),
|
||||||
|
GestureStep::Wait(GestureStepWait {
|
||||||
|
wait_for_animation: true,
|
||||||
|
wait_for_time: true,
|
||||||
|
wait_time: 1.25,
|
||||||
|
}),
|
||||||
|
GestureStep::EOF(GestureStepEOF),
|
||||||
|
];
|
||||||
|
gesture.encode().unwrap();
|
||||||
|
let mut decoded =
|
||||||
|
AssetGesture::new_with_uuid_bytes(UUID::random().unwrap(), gesture.asset_data.clone())
|
||||||
|
.unwrap();
|
||||||
|
assert!(decoded.decode().unwrap());
|
||||||
|
assert_eq!(decoded.trigger_key, gesture.trigger_key);
|
||||||
|
assert_eq!(decoded.trigger_key_mask, gesture.trigger_key_mask);
|
||||||
|
assert_eq!(decoded.trigger, gesture.trigger);
|
||||||
|
assert_eq!(decoded.replace_with, gesture.replace_with);
|
||||||
|
assert_eq!(decoded.sequence, gesture.sequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn malformed_gesture_returns_false_without_partial_sequence() {
|
||||||
|
let mut gesture =
|
||||||
|
AssetGesture::new_with_uuid_bytes(UUID::zero(), b"1\n0\n".to_vec()).unwrap();
|
||||||
|
assert!(!gesture.decode().unwrap());
|
||||||
|
assert!(gesture.sequence.is_empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,12 +4,15 @@ extern crate self as libremetaverse;
|
|||||||
|
|
||||||
#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
|
#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
|
||||||
mod attention_catalog;
|
mod attention_catalog;
|
||||||
|
mod agent_manager;
|
||||||
|
mod agent_messages;
|
||||||
mod bit_pack;
|
mod bit_pack;
|
||||||
mod caps;
|
mod caps;
|
||||||
mod caps_http;
|
mod caps_http;
|
||||||
mod client_core;
|
mod client_core;
|
||||||
mod download_manager;
|
mod download_manager;
|
||||||
mod event_queue;
|
mod event_queue;
|
||||||
|
mod gesture;
|
||||||
#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
|
#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
|
||||||
mod foliage_catalog;
|
mod foliage_catalog;
|
||||||
mod generated;
|
mod generated;
|
||||||
@@ -154,18 +157,6 @@ impl InventoryAISClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
impl AgentManager {
|
|
||||||
fn extract_experience_permission(
|
|
||||||
_map: libremetaverse_structured_data::OSDMap,
|
|
||||||
_experience_id: libremetaverse_types::UUID,
|
|
||||||
) -> Result<String, Error> {
|
|
||||||
libremetaverse_types::not_implemented(
|
|
||||||
"M:LibreMetaverse.AgentManager.ExtractExperiencePermission(LibreMetaverse.StructuredData.OSDMap,LibreMetaverse.UUID)",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code, clippy::unused_self)]
|
#[allow(dead_code, clippy::unused_self)]
|
||||||
impl EstateTools {
|
impl EstateTools {
|
||||||
fn estate_owner_message_handler(
|
fn estate_owner_message_handler(
|
||||||
|
|||||||
@@ -295,6 +295,13 @@ pub struct PacketReceivedEventArgs {
|
|||||||
simulator: Simulator,
|
simulator: Simulator,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct RawPacketReceivedEventArgs {
|
||||||
|
pub packet_type: PacketType,
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
pub simulator: Simulator,
|
||||||
|
}
|
||||||
|
|
||||||
impl PacketReceivedEventArgs {
|
impl PacketReceivedEventArgs {
|
||||||
pub fn new(packet: Packet, simulator: Simulator) -> Result<Self, Error> {
|
pub fn new(packet: Packet, simulator: Simulator) -> Result<Self, Error> {
|
||||||
Ok(Self { packet, simulator })
|
Ok(Self { packet, simulator })
|
||||||
@@ -1201,6 +1208,12 @@ impl Simulator {
|
|||||||
Arc::clone(&self.data)
|
Arc::clone(&self.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn native_caps(&self) -> Option<Caps> {
|
||||||
|
read(&self.data.caps_state)
|
||||||
|
.clone()
|
||||||
|
.map(|inner| Caps::native_from_inner(inner, self.native_clone_without_caps()))
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn native_from_weak(data: &Weak<SimulatorData>) -> Option<Self> {
|
pub(crate) fn native_from_weak(data: &Weak<SimulatorData>) -> Option<Self> {
|
||||||
data.upgrade().map(|data| Self { caps: None, data })
|
data.upgrade().map(|data| Self { caps: None, data })
|
||||||
}
|
}
|
||||||
@@ -1775,6 +1788,7 @@ struct NetworkEvents {
|
|||||||
logged_out: EventRegistry<LoggedOutEventArgs>,
|
logged_out: EventRegistry<LoggedOutEventArgs>,
|
||||||
login_progress: EventRegistry<LoginProgressEventArgs>,
|
login_progress: EventRegistry<LoginProgressEventArgs>,
|
||||||
packet_sent: EventRegistry<PacketSentEventArgs>,
|
packet_sent: EventRegistry<PacketSentEventArgs>,
|
||||||
|
raw_packet_received: EventRegistry<RawPacketReceivedEventArgs>,
|
||||||
sim_changed: EventRegistry<SimChangedEventArgs>,
|
sim_changed: EventRegistry<SimChangedEventArgs>,
|
||||||
sim_connected: EventRegistry<SimConnectedEventArgs>,
|
sim_connected: EventRegistry<SimConnectedEventArgs>,
|
||||||
sim_connecting: EventRegistry<SimConnectingEventArgs>,
|
sim_connecting: EventRegistry<SimConnectingEventArgs>,
|
||||||
@@ -1870,6 +1884,15 @@ impl NetworkManagerInner {
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
inner.process_internal_packet(&packet, &simulator, raw_data.as_deref());
|
inner.process_internal_packet(&packet, &simulator, raw_data.as_deref());
|
||||||
|
if let Some(data) = raw_data {
|
||||||
|
inner.events.raw_packet_received.emit_with(|| {
|
||||||
|
RawPacketReceivedEventArgs {
|
||||||
|
packet_type: packet.type_,
|
||||||
|
data: data.clone(),
|
||||||
|
simulator: simulator.clone(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
let _ =
|
let _ =
|
||||||
inner
|
inner
|
||||||
.packet_events
|
.packet_events
|
||||||
@@ -2378,6 +2401,7 @@ impl Drop for NetworkManagerInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Native implementation of C# `NetworkManager`.
|
/// Native implementation of C# `NetworkManager`.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct NetworkManager {
|
pub struct NetworkManager {
|
||||||
pub login_response_data: Option<LoginResponseData>,
|
pub login_response_data: Option<LoginResponseData>,
|
||||||
pub simulators: SimulatorCollection,
|
pub simulators: SimulatorCollection,
|
||||||
@@ -2408,6 +2432,14 @@ impl NetworkManager {
|
|||||||
Arc::downgrade(&self.inner)
|
Arc::downgrade(&self.inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn native_agent_id(&self) -> UUID {
|
||||||
|
*read(&self.inner.agent_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn native_session_id(&self) -> UUID {
|
||||||
|
*read(&self.inner.session_id)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn native_new(client: GridClient) -> Result<Self, Error> {
|
pub fn native_new(client: GridClient) -> Result<Self, Error> {
|
||||||
let simulators = SimulatorCollection::default();
|
let simulators = SimulatorCollection::default();
|
||||||
let packet_events = PacketEventDictionary::new(client.clone())?;
|
let packet_events = PacketEventDictionary::new(client.clone())?;
|
||||||
@@ -3176,6 +3208,13 @@ impl NetworkManager {
|
|||||||
.subscribe(packet_type, handler, is_async)
|
.subscribe(packet_type, handler, is_async)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn subscribe_raw_packet(
|
||||||
|
&self,
|
||||||
|
handler: EventHandler<RawPacketReceivedEventArgs>,
|
||||||
|
) -> Subscription {
|
||||||
|
self.inner.events.raw_packet_received.subscribe(handler)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn subscribe_caps(
|
pub fn subscribe_caps(
|
||||||
&self,
|
&self,
|
||||||
caps_event: String,
|
caps_event: String,
|
||||||
|
|||||||
49
docs/agent-manager.md
Normal file
49
docs/agent-manager.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# Agent manager compatibility contract
|
||||||
|
|
||||||
|
The native `AgentManager` implements LibreMetaverse's non-movement agent
|
||||||
|
services over the native network and capability layers. It owns a strong
|
||||||
|
network-manager reference until `Dispose`, so UDP and event-queue callbacks do
|
||||||
|
not disappear when the weak `GridClient` service cache is the only other
|
||||||
|
reference.
|
||||||
|
|
||||||
|
## Protocol selection and state
|
||||||
|
|
||||||
|
Chat, instant messages, conferences, gesture activation, animation, viewer
|
||||||
|
effects, balance requests, payments, profile updates, interests, picks,
|
||||||
|
classifieds, mute changes, and their replies retain the C# LLUDP packet choice
|
||||||
|
and wire fields. Capability-backed preferences, experiences, display names,
|
||||||
|
offline messages, attachment resources, viewer benefits, render information,
|
||||||
|
viewer statistics, postcards, and nav-mesh status use LLSD through the current
|
||||||
|
simulator's advertised capability. A missing optional capability produces the
|
||||||
|
same empty/`None` result as the reference; caller cancellation remains a typed
|
||||||
|
`Error::Cancelled`.
|
||||||
|
|
||||||
|
Incoming UDP and capability handlers update synchronized state before taking a
|
||||||
|
snapshot of the subscriber list. User callbacks therefore run without an
|
||||||
|
agent-state lock held. Group-chat membership, signaled animations, balance,
|
||||||
|
identity/group data, mute entries, preferences, benefits, and the last nav-mesh
|
||||||
|
status are shared between all `GridClient::Self` snapshots.
|
||||||
|
`Debug` output exposes only message and binary-payload lengths, never chat/IM
|
||||||
|
text, transaction descriptions, or capability URLs.
|
||||||
|
|
||||||
|
Mute lists are downloaded with the legacy RequestXfer/SendXferPacket protocol.
|
||||||
|
The receiver validates the agent, acknowledges ordered chunks, rejects
|
||||||
|
oversized or inconsistent transfers, replaces state only after a complete
|
||||||
|
download, and then raises `MuteListUpdated`.
|
||||||
|
|
||||||
|
## Gesture execution
|
||||||
|
|
||||||
|
Gesture assets use the C# version-2 text format. `PlayGestureAsync` first uses
|
||||||
|
`ViewerAsset` and falls back to the LLUDP asset-transfer protocol. It executes
|
||||||
|
chat-channel prefixes, animation start/stop, sound triggers, and timed waits in
|
||||||
|
sequence. The reference's animation-completion wait remains a no-op. Downloads
|
||||||
|
are bounded to 16 MiB, the whole operation is bounded to 30 seconds, and a
|
||||||
|
cancelled UDP transfer sends an abort packet.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
Unit tests cover UTF-8 chat splitting, identity and animation state, group-chat
|
||||||
|
ordering, invitation/display-name events, mute parsing, nav-mesh state, gesture
|
||||||
|
format round trips, and malformed assets. The offline fake-grid test performs
|
||||||
|
real seed discovery, serves a gesture through `ViewerAsset`, and observes the
|
||||||
|
resulting chat, animation, and sound packets on a loopback UDP peer.
|
||||||
95
tools/check_milestone_09_issue_59.py
Normal file
95
tools/check_milestone_09_issue_59.py
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Audit issue 59's native non-movement AgentManager boundary."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
|
||||||
|
import generate_api_shims
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
SOURCE = ROOT / "crates" / "libremetaverse" / "src" / "agent_manager.rs"
|
||||||
|
GESTURE = ROOT / "crates" / "libremetaverse" / "src" / "gesture.rs"
|
||||||
|
DOC = ROOT / "docs" / "agent-manager.md"
|
||||||
|
STUB_RE = re.compile(r"\b(?:not_implemented|unimplemented_api)\b|\b(?:todo|unimplemented)!\s*\(")
|
||||||
|
|
||||||
|
REQUIRED_TYPES = {
|
||||||
|
"T:LibreMetaverse.AgentManager",
|
||||||
|
"T:LibreMetaverse.InstantMessage",
|
||||||
|
"T:LibreMetaverse.ChatSessionMember",
|
||||||
|
"T:LibreMetaverse.Assets.AssetGesture",
|
||||||
|
"T:LibreMetaverse.Assets.GestureStepAnimation",
|
||||||
|
"T:LibreMetaverse.Assets.GestureStepChat",
|
||||||
|
"T:LibreMetaverse.Assets.GestureStepEOF",
|
||||||
|
"T:LibreMetaverse.Assets.GestureStepSound",
|
||||||
|
"T:LibreMetaverse.Assets.GestureStepWait",
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRED_MEMBERS = {
|
||||||
|
"E:LibreMetaverse.AgentManager.IM",
|
||||||
|
"E:LibreMetaverse.AgentManager.ChatFromSimulator",
|
||||||
|
"E:LibreMetaverse.AgentManager.MuteListUpdated",
|
||||||
|
"E:LibreMetaverse.AgentManager.NavMeshStatusUpdate",
|
||||||
|
"M:LibreMetaverse.AgentManager.Chat(System.String,System.Int32,LibreMetaverse.ChatType,System.Boolean)",
|
||||||
|
"M:LibreMetaverse.AgentManager.PlayGestureAsync(LibreMetaverse.UUID,System.Threading.CancellationToken)",
|
||||||
|
"M:LibreMetaverse.AgentManager.RetrieveInstantMessagesAsync(System.Threading.CancellationToken)",
|
||||||
|
"M:LibreMetaverse.AgentManager.GetAgentPreferencesAsync(System.Threading.CancellationToken)",
|
||||||
|
"M:LibreMetaverse.AgentManager.GetAgentExperiencesAsync(System.Threading.CancellationToken)",
|
||||||
|
"M:LibreMetaverse.AgentManager.GetAttachmentResourcesAsync(System.Threading.CancellationToken)",
|
||||||
|
"M:LibreMetaverse.AgentManager.GetViewerBenefitsAsync(System.Threading.CancellationToken)",
|
||||||
|
"M:LibreMetaverse.AgentManager.RequestMuteList",
|
||||||
|
"M:LibreMetaverse.AgentManager.RequestNavMeshGenerationStatusAsync(System.Threading.CancellationToken)",
|
||||||
|
"M:LibreMetaverse.AgentManager.RequestNavMeshRebakeAsync(System.Threading.CancellationToken)",
|
||||||
|
"M:LibreMetaverse.AgentManager.SendViewerStatsAsync(LibreMetaverse.Messages.Linden.ViewerStatsMessage,System.Threading.CancellationToken)",
|
||||||
|
"M:LibreMetaverse.AgentManager.UpdateProfileAsync(LibreMetaverse.Avatar.AvatarProperties,System.Threading.CancellationToken)",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def require_markers(path: Path, markers: tuple[str, ...]) -> None:
|
||||||
|
source = path.read_text()
|
||||||
|
missing = [marker for marker in markers if marker not in source]
|
||||||
|
if missing:
|
||||||
|
raise SystemExit(f"{path.name}: audit evidence missing: " + ", ".join(missing))
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
native_types = (
|
||||||
|
generate_api_shims.NATIVE_TYPES.keys()
|
||||||
|
| generate_api_shims.NATIVE_DECLARATIONS.keys()
|
||||||
|
)
|
||||||
|
missing_types = sorted(REQUIRED_TYPES - native_types)
|
||||||
|
if missing_types:
|
||||||
|
raise SystemExit("issue 59 native types missing: " + ", ".join(missing_types))
|
||||||
|
missing_members = sorted(REQUIRED_MEMBERS - generate_api_shims.NATIVE_MEMBER_BODIES.keys())
|
||||||
|
if missing_members:
|
||||||
|
raise SystemExit("issue 59 native members missing: " + ", ".join(missing_members))
|
||||||
|
|
||||||
|
for path in (SOURCE, GESTURE):
|
||||||
|
if STUB_RE.search(path.read_text()):
|
||||||
|
raise SystemExit(f"issue 59 owned Rust stubs remain in {path.name}")
|
||||||
|
|
||||||
|
require_markers(
|
||||||
|
SOURCE,
|
||||||
|
(
|
||||||
|
"ChatterBoxSessionAgentListUpdates",
|
||||||
|
"MuteListUpdate",
|
||||||
|
"NavMeshStatusUpdate",
|
||||||
|
"MAX_GESTURE_ASSET_BYTES",
|
||||||
|
"MAX_MUTE_LIST_BYTES",
|
||||||
|
"fake_grid_serves_gesture_capability_and_observes_udp_actions",
|
||||||
|
"instant_message_debug_redacts_message_and_binary_payload",
|
||||||
|
"callbacks are not invoked",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
require_markers(GESTURE, ("gesture_v2_roundtrip_preserves_all_step_payloads", "decode"))
|
||||||
|
require_markers(DOC, ("RequestXfer", "ViewerAsset", "callbacks therefore run without"))
|
||||||
|
print(
|
||||||
|
"issue 59 audit: native AgentManager mappings, bounded gesture/mute transfers, "
|
||||||
|
"lock-free callback evidence, documentation, and offline fake-grid coverage are present"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -40,6 +40,13 @@ TARGETS = {
|
|||||||
NATIVE_TYPES = {
|
NATIVE_TYPES = {
|
||||||
"T:LibreMetaverse.AccountLevelBenefits": "crate::login::AccountLevelBenefits",
|
"T:LibreMetaverse.AccountLevelBenefits": "crate::login::AccountLevelBenefits",
|
||||||
"T:LibreMetaverse.AgentDisplayName": "crate::message_decoder::AgentDisplayName",
|
"T:LibreMetaverse.AgentDisplayName": "crate::message_decoder::AgentDisplayName",
|
||||||
|
"T:LibreMetaverse.AgentDataReplyEventArgs": "crate::agent_messages::AgentDataReplyEventArgs",
|
||||||
|
"T:LibreMetaverse.AgentAccessEventArgs": "crate::agent_messages::AgentAccessEventArgs",
|
||||||
|
"T:LibreMetaverse.AgentExperiencesEventArgs": "crate::agent_messages::AgentExperiencesEventArgs",
|
||||||
|
"T:LibreMetaverse.AgentPreferencesEventArgs": "crate::agent_messages::AgentPreferencesEventArgs",
|
||||||
|
"T:LibreMetaverse.AnimationsChangedEventArgs": "crate::agent_messages::AnimationsChangedEventArgs",
|
||||||
|
"T:LibreMetaverse.AvatarRenderInfoEventArgs": "crate::agent_messages::AvatarRenderInfoEventArgs",
|
||||||
|
"T:LibreMetaverse.BalanceEventArgs": "crate::agent_messages::BalanceEventArgs",
|
||||||
"T:LibreMetaverse.CapabilitiesReceivedEventArgs": "crate::caps::CapabilitiesReceivedEventArgs",
|
"T:LibreMetaverse.CapabilitiesReceivedEventArgs": "crate::caps::CapabilitiesReceivedEventArgs",
|
||||||
"T:LibreMetaverse.Caps": "crate::caps::Caps",
|
"T:LibreMetaverse.Caps": "crate::caps::Caps",
|
||||||
"T:LibreMetaverse.CapsRateLimiter": "crate::caps_http::CapsRateLimiter",
|
"T:LibreMetaverse.CapsRateLimiter": "crate::caps_http::CapsRateLimiter",
|
||||||
@@ -51,6 +58,40 @@ NATIVE_TYPES = {
|
|||||||
"T:LibreMetaverse.Http.EventQueueClient.EventCallback": "crate::event_queue::EventQueueClientEventCallback",
|
"T:LibreMetaverse.Http.EventQueueClient.EventCallback": "crate::event_queue::EventQueueClientEventCallback",
|
||||||
"T:LibreMetaverse.HttpCapsClient": "crate::caps_http::HttpCapsClient",
|
"T:LibreMetaverse.HttpCapsClient": "crate::caps_http::HttpCapsClient",
|
||||||
"T:LibreMetaverse.HttpCapsClient.ProgressReport": "crate::caps_http::HttpCapsClientProgressReport",
|
"T:LibreMetaverse.HttpCapsClient.ProgressReport": "crate::caps_http::HttpCapsClientProgressReport",
|
||||||
|
"T:LibreMetaverse.InstantMessage": "crate::agent_manager::InstantMessage",
|
||||||
|
"T:LibreMetaverse.InstantMessageEventArgs": "crate::agent_manager::InstantMessageEventArgs",
|
||||||
|
"T:LibreMetaverse.ChatEventArgs": "crate::agent_messages::ChatEventArgs",
|
||||||
|
"T:LibreMetaverse.ChatSessionMember": "crate::agent_messages::ChatSessionMember",
|
||||||
|
"T:LibreMetaverse.ChatSessionMemberAddedEventArgs": "crate::agent_messages::ChatSessionMemberAddedEventArgs",
|
||||||
|
"T:LibreMetaverse.ChatSessionMemberLeftEventArgs": "crate::agent_messages::ChatSessionMemberLeftEventArgs",
|
||||||
|
"T:LibreMetaverse.Assets.AssetGesture": "crate::gesture::AssetGesture",
|
||||||
|
"T:LibreMetaverse.Assets.GestureStep": "crate::gesture::GestureStep",
|
||||||
|
"T:LibreMetaverse.Assets.GestureStepAnimation": "crate::gesture::GestureStepAnimation",
|
||||||
|
"T:LibreMetaverse.Assets.GestureStepChat": "crate::gesture::GestureStepChat",
|
||||||
|
"T:LibreMetaverse.Assets.GestureStepEOF": "crate::gesture::GestureStepEOF",
|
||||||
|
"T:LibreMetaverse.Assets.GestureStepSound": "crate::gesture::GestureStepSound",
|
||||||
|
"T:LibreMetaverse.Assets.GestureStepWait": "crate::gesture::GestureStepWait",
|
||||||
|
"T:LibreMetaverse.ExperienceInfoEventArgs": "crate::agent_messages::ExperienceInfoEventArgs",
|
||||||
|
"T:LibreMetaverse.ExperiencePreferencesEventArgs": "crate::agent_messages::ExperiencePreferencesEventArgs",
|
||||||
|
"T:LibreMetaverse.GroupChatJoinedEventArgs": "crate::agent_messages::GroupChatJoinedEventArgs",
|
||||||
|
"T:LibreMetaverse.RegionExperiencesEventArgs": "crate::agent_messages::RegionExperiencesEventArgs",
|
||||||
|
"T:LibreMetaverse.SetDisplayNameReplyEventArgs": "crate::agent_messages::SetDisplayNameReplyEventArgs",
|
||||||
|
"T:LibreMetaverse.ProductInfoEventArgs": "crate::agent_messages::ProductInfoEventArgs",
|
||||||
|
"T:LibreMetaverse.ViewerBenefitsEventArgs": "crate::agent_messages::ViewerBenefitsEventArgs",
|
||||||
|
"T:LibreMetaverse.Messages.Linden.ProductInfoRequestMessage": "crate::agent_manager::ProductInfoRequestMessage",
|
||||||
|
"T:LibreMetaverse.Messages.Linden.ProductInfoRequestMessage.ProductInfo": "crate::agent_manager::ProductInfoRequestMessageProductInfo",
|
||||||
|
"T:LibreMetaverse.Messages.Linden.ExperienceListMessage": "crate::agent_messages::ExperienceListMessage",
|
||||||
|
"T:LibreMetaverse.Messages.Linden.ExperiencePreferencesMessage": "crate::agent_messages::ExperiencePreferencesMessage",
|
||||||
|
"T:LibreMetaverse.Messages.Linden.RegionExperiencesMessage": "crate::agent_messages::RegionExperiencesMessage",
|
||||||
|
"T:LibreMetaverse.Messages.Linden.AgentPreferencesMessage": "crate::agent_messages::AgentPreferencesMessage",
|
||||||
|
"T:LibreMetaverse.Messages.Linden.AvatarRenderInfoMessage": "crate::agent_messages::AvatarRenderInfoMessage",
|
||||||
|
"T:LibreMetaverse.Messages.Linden.AvatarRenderInfoMessage.AvatarInfo": "crate::agent_messages::AvatarRenderInfoMessageAvatarInfo",
|
||||||
|
"T:LibreMetaverse.Messages.Linden.ExperienceInfo": "crate::agent_messages::ExperienceInfo",
|
||||||
|
"T:LibreMetaverse.Messages.Linden.ExperienceInfoMessage": "crate::agent_messages::ExperienceInfoMessage",
|
||||||
|
"T:LibreMetaverse.Messages.Linden.ViewerBenefitsMessage": "crate::agent_messages::ViewerBenefitsMessage",
|
||||||
|
"T:LibreMetaverse.Messages.Linden.ViewerBenefitsMessage.BenefitPackage": "crate::agent_messages::ViewerBenefitsMessageBenefitPackage",
|
||||||
|
"T:LibreMetaverse.MoneyBalanceReplyEventArgs": "crate::agent_messages::MoneyBalanceReplyEventArgs",
|
||||||
|
"T:LibreMetaverse.NavMeshStatusUpdateEventArgs": "crate::agent_messages::NavMeshStatusUpdateEventArgs",
|
||||||
"T:LibreMetaverse.LoginCredential": "crate::login::LoginCredential",
|
"T:LibreMetaverse.LoginCredential": "crate::login::LoginCredential",
|
||||||
"T:LibreMetaverse.LoginException": "crate::login::LoginException",
|
"T:LibreMetaverse.LoginException": "crate::login::LoginException",
|
||||||
"T:LibreMetaverse.LoginParams": "crate::login::LoginParams",
|
"T:LibreMetaverse.LoginParams": "crate::login::LoginParams",
|
||||||
@@ -176,6 +217,7 @@ 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.AgentManager": "crate::agent_manager::AgentManager",
|
||||||
"T:LibreMetaverse.GridClient": "crate::client_core::GridClient",
|
"T:LibreMetaverse.GridClient": "crate::client_core::GridClient",
|
||||||
"T:LibreMetaverse.NetworkManager": "crate::network_manager::NetworkManager",
|
"T:LibreMetaverse.NetworkManager": "crate::network_manager::NetworkManager",
|
||||||
"T:LibreMetaverse.Settings": "crate::client_core::Settings",
|
"T:LibreMetaverse.Settings": "crate::client_core::Settings",
|
||||||
@@ -184,6 +226,36 @@ NATIVE_DECLARATIONS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NATIVE_MEMBER_BODIES = {
|
NATIVE_MEMBER_BODIES = {
|
||||||
|
"M:LibreMetaverse.Avatar.AvatarProperties.FromOSD(LibreMetaverse.StructuredData.OSD)":
|
||||||
|
"crate::agent_messages::avatar_properties_from_osd(o)",
|
||||||
|
"M:LibreMetaverse.Avatar.AvatarProperties.GetOSD":
|
||||||
|
"crate::agent_messages::avatar_properties_get_osd(self)",
|
||||||
|
"P:LibreMetaverse.Avatar.AvatarProperties.AllowPublish":
|
||||||
|
"self.flags.0 & crate::ProfileFlags::ALLOW_PUBLISH.0 != 0",
|
||||||
|
"P:LibreMetaverse.Avatar.AvatarProperties.AllowPublish#set":
|
||||||
|
"if value { self.flags.0 |= crate::ProfileFlags::ALLOW_PUBLISH.0 } else { self.flags.0 &= !crate::ProfileFlags::ALLOW_PUBLISH.0 }",
|
||||||
|
"P:LibreMetaverse.Avatar.AvatarProperties.Identified":
|
||||||
|
"self.flags.0 & crate::ProfileFlags::IDENTIFIED.0 != 0",
|
||||||
|
"P:LibreMetaverse.Avatar.AvatarProperties.Identified#set":
|
||||||
|
"if value { self.flags.0 |= crate::ProfileFlags::IDENTIFIED.0 } else { self.flags.0 &= !crate::ProfileFlags::IDENTIFIED.0 }",
|
||||||
|
"P:LibreMetaverse.Avatar.AvatarProperties.MaturePublish":
|
||||||
|
"self.flags.0 & crate::ProfileFlags::MATURE_PUBLISH.0 != 0",
|
||||||
|
"P:LibreMetaverse.Avatar.AvatarProperties.MaturePublish#set":
|
||||||
|
"if value { self.flags.0 |= crate::ProfileFlags::MATURE_PUBLISH.0 } else { self.flags.0 &= !crate::ProfileFlags::MATURE_PUBLISH.0 }",
|
||||||
|
"P:LibreMetaverse.Avatar.AvatarProperties.Online":
|
||||||
|
"self.flags.0 & crate::ProfileFlags::ONLINE.0 != 0",
|
||||||
|
"P:LibreMetaverse.Avatar.AvatarProperties.Online#set":
|
||||||
|
"if value { self.flags.0 |= crate::ProfileFlags::ONLINE.0 } else { self.flags.0 &= !crate::ProfileFlags::ONLINE.0 }",
|
||||||
|
"P:LibreMetaverse.Avatar.AvatarProperties.Transacted":
|
||||||
|
"self.flags.0 & crate::ProfileFlags::TRANSACTED.0 != 0",
|
||||||
|
"P:LibreMetaverse.Avatar.AvatarProperties.Transacted#set":
|
||||||
|
"if value { self.flags.0 |= crate::ProfileFlags::TRANSACTED.0 } else { self.flags.0 &= !crate::ProfileFlags::TRANSACTED.0 }",
|
||||||
|
"M:LibreMetaverse.Avatar.Interests.FromOSD(LibreMetaverse.StructuredData.OSD)":
|
||||||
|
"crate::agent_messages::avatar_interests_from_osd(o)",
|
||||||
|
"M:LibreMetaverse.Avatar.Interests.GetOSD":
|
||||||
|
"crate::agent_messages::avatar_interests_get_osd(self)",
|
||||||
|
"M:LibreMetaverse.TransactionInfo.#ctor":
|
||||||
|
"Ok(crate::TransactionInfo { amount: 0, dest_id: libremetaverse_types::UUID::zero(), is_dest_group: false, is_source_group: false, item_description: String::new(), source_id: libremetaverse_types::UUID::zero(), transaction_type: 0 })",
|
||||||
"M:LibreMetaverse.AgentSettings.#ctor":
|
"M:LibreMetaverse.AgentSettings.#ctor":
|
||||||
"Ok(crate::client_core::agent_settings_defaults())",
|
"Ok(crate::client_core::agent_settings_defaults())",
|
||||||
"M:LibreMetaverse.AssetCacheSettings.#ctor":
|
"M:LibreMetaverse.AssetCacheSettings.#ctor":
|
||||||
@@ -222,6 +294,290 @@ NATIVE_MEMBER_BODIES = {
|
|||||||
"self.native_network().unwrap_or_else(|_| panic!(\"failed to construct NetworkManager\"))",
|
"self.native_network().unwrap_or_else(|_| panic!(\"failed to construct NetworkManager\"))",
|
||||||
"P:LibreMetaverse.GridClient.Network#set":
|
"P:LibreMetaverse.GridClient.Network#set":
|
||||||
"self.native_set_network(value)",
|
"self.native_set_network(value)",
|
||||||
|
"P:LibreMetaverse.GridClient.Self":
|
||||||
|
"self.native_self()",
|
||||||
|
"E:LibreMetaverse.AgentManager.IM":
|
||||||
|
"self.native_subscribe_im(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.AgentDataReply":
|
||||||
|
"self.native_subscribe_agent_data_reply(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.AnimationsChanged":
|
||||||
|
"self.native_subscribe_animations_changed(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.ChatFromSimulator":
|
||||||
|
"self.native_subscribe_chat_from_simulator(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.MoneyBalance":
|
||||||
|
"self.native_subscribe_money_balance(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.MoneyBalanceReply":
|
||||||
|
"self.native_subscribe_money_balance_reply(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.AgentExperiencesUpdated":
|
||||||
|
"self.native_subscribe_agent_experiences_updated(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.ExperiencePreferencesUpdated":
|
||||||
|
"self.native_subscribe_experience_preferences_updated(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.RegionExperiencesUpdated":
|
||||||
|
"self.native_subscribe_region_experiences_updated(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.ExperienceInfoReceived":
|
||||||
|
"self.native_subscribe_experience_info_received(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.AgentPreferencesUpdated":
|
||||||
|
"self.native_subscribe_agent_preferences_updated(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.AvatarRenderInfoUpdated":
|
||||||
|
"self.native_subscribe_avatar_render_info_updated(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.ViewerBenefitsUpdated":
|
||||||
|
"self.native_subscribe_viewer_benefits_updated(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.ProductInfoUpdated":
|
||||||
|
"self.native_subscribe_product_info_updated(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.MuteListUpdated":
|
||||||
|
"self.native_subscribe_mute_list_updated(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.GroupChatJoined":
|
||||||
|
"self.native_subscribe_group_chat_joined(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.ChatSessionMemberAdded":
|
||||||
|
"self.native_subscribe_chat_session_member_added(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.ChatSessionMemberLeft":
|
||||||
|
"self.native_subscribe_chat_session_member_left(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.SetDisplayNameReply":
|
||||||
|
"self.native_subscribe_set_display_name_reply(handler)",
|
||||||
|
"E:LibreMetaverse.AgentManager.NavMeshStatusUpdate":
|
||||||
|
"self.native_subscribe_nav_mesh_status_update(handler)",
|
||||||
|
"M:LibreMetaverse.AgentManager.#ctor(LibreMetaverse.GridClient)":
|
||||||
|
"crate::agent_manager::AgentManager::native_new(client)",
|
||||||
|
"M:LibreMetaverse.AgentManager.Dispose":
|
||||||
|
"self.native_dispose()",
|
||||||
|
"M:LibreMetaverse.AgentManager.ActivateGesture(LibreMetaverse.UUID,LibreMetaverse.UUID)":
|
||||||
|
"self.native_activate_gesture(inv_id, asset_id)",
|
||||||
|
"M:LibreMetaverse.AgentManager.Animate(System.Collections.Generic.Dictionary{LibreMetaverse.UUID,System.Boolean},System.Boolean)":
|
||||||
|
"self.native_animate(animations, reliable)",
|
||||||
|
"M:LibreMetaverse.AgentManager.AnimationStart(LibreMetaverse.UUID,System.Boolean)":
|
||||||
|
"self.native_animation_start(animation, reliable)",
|
||||||
|
"M:LibreMetaverse.AgentManager.AnimationStop(LibreMetaverse.UUID,System.Boolean)":
|
||||||
|
"self.native_animation_stop(animation, reliable)",
|
||||||
|
"M:LibreMetaverse.AgentManager.BeamEffect(LibreMetaverse.UUID,LibreMetaverse.UUID,LibreMetaverse.Vector3d,LibreMetaverse.Color4,System.Single,LibreMetaverse.UUID)":
|
||||||
|
"self.native_viewer_effect(source_avatar, target_object, global_offset, color, duration, effect_id, crate::EffectType::Beam, None)",
|
||||||
|
"M:LibreMetaverse.AgentManager.Chat(System.String,System.Int32,LibreMetaverse.ChatType,System.Boolean)":
|
||||||
|
"self.native_chat(message, channel, type_, split_large_messages)",
|
||||||
|
"M:LibreMetaverse.AgentManager.ChatterBoxAcceptInviteAsync(LibreMetaverse.UUID,System.Threading.CancellationToken)":
|
||||||
|
"self.native_chatter_box_accept_invite(session_id, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.DeactivateGesture(LibreMetaverse.UUID)":
|
||||||
|
"self.native_deactivate_gesture(inv_id)",
|
||||||
|
"M:LibreMetaverse.AgentManager.DeleteClassified(LibreMetaverse.UUID)":
|
||||||
|
"self.native_delete_classified(classified_id)",
|
||||||
|
"M:LibreMetaverse.AgentManager.FindExperienceByNameAsync(System.String,System.Int32,System.Int32,System.Threading.CancellationToken)":
|
||||||
|
"self.native_find_experience_by_name(query, page, page_size, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.ForgetExperiencePermissionAsync(LibreMetaverse.UUID,System.Threading.CancellationToken)":
|
||||||
|
"self.native_forget_experience_permission(experience_id, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.GetAdminExperiencesAsync(System.Threading.CancellationToken)":
|
||||||
|
"self.native_get_admin_experiences(cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.GetAgentExperiencePermissionsAsync(System.Threading.CancellationToken)":
|
||||||
|
"self.native_get_agent_experience_permissions(cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.GetAgentExperiencesAsync(System.Threading.CancellationToken)":
|
||||||
|
"self.native_get_agent_experiences(cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.GetAgentPreferencesAsync(System.Threading.CancellationToken)":
|
||||||
|
"self.native_get_agent_preferences(cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.GetAttachmentResourcesAsync(System.Threading.CancellationToken)":
|
||||||
|
"self.native_get_attachment_resources(cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.GetAvatarRenderInfoAsync(System.Threading.CancellationToken)":
|
||||||
|
"self.native_get_avatar_render_info(cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.GetCreatorExperiencesAsync(System.Threading.CancellationToken)":
|
||||||
|
"self.native_get_creator_experiences(cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.GetExperienceInfoAsync(System.Collections.Generic.IEnumerable{LibreMetaverse.UUID},System.Threading.CancellationToken)":
|
||||||
|
"self.native_get_experience_info(experience_ids, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.GetExperiencePermissionAsync(LibreMetaverse.UUID,System.Threading.CancellationToken)":
|
||||||
|
"self.native_get_experience_permission(experience_id, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.GetExperiencePreferencesAsync(System.Threading.CancellationToken)":
|
||||||
|
"self.native_get_experience_preferences(cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.GetGroupExperiencesAsync(LibreMetaverse.UUID,System.Threading.CancellationToken)":
|
||||||
|
"self.native_get_group_experiences(group_id, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.GetProductInfoAsync(System.Threading.CancellationToken)":
|
||||||
|
"self.native_get_product_info(cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.GetRegionExperiencesAsync(System.Threading.CancellationToken)":
|
||||||
|
"self.native_get_region_experiences(cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.GetViewerBenefitsAsync(System.Threading.CancellationToken)":
|
||||||
|
"self.native_get_viewer_benefits(cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.GiveAvatarMoney(LibreMetaverse.UUID,System.Int32)":
|
||||||
|
"self.native_give_avatar_money(target, amount, String::new())",
|
||||||
|
"M:LibreMetaverse.AgentManager.GiveAvatarMoney(LibreMetaverse.UUID,System.Int32,System.String)":
|
||||||
|
"self.native_give_avatar_money(target, amount, description)",
|
||||||
|
"M:LibreMetaverse.AgentManager.GiveGroupMoney(LibreMetaverse.UUID,System.Int32)":
|
||||||
|
"self.native_give_group_money(target, amount, String::new())",
|
||||||
|
"M:LibreMetaverse.AgentManager.GiveGroupMoney(LibreMetaverse.UUID,System.Int32,System.String)":
|
||||||
|
"self.native_give_group_money(target, amount, description)",
|
||||||
|
"M:LibreMetaverse.AgentManager.GiveMoney(LibreMetaverse.UUID,System.Int32,System.String,LibreMetaverse.MoneyTransactionType,LibreMetaverse.TransactionFlags)":
|
||||||
|
"self.native_give_money(target, amount, description, type_, flags)",
|
||||||
|
"M:LibreMetaverse.AgentManager.GiveObjectMoney(LibreMetaverse.UUID,System.Int32,System.String)":
|
||||||
|
"self.native_give_object_money(target, amount, object_name)",
|
||||||
|
"M:LibreMetaverse.AgentManager.GroupInviteRespond(LibreMetaverse.UUID,LibreMetaverse.UUID,System.Boolean)":
|
||||||
|
"self.native_group_invite_respond(group_id, im_session_id, accept)",
|
||||||
|
"M:LibreMetaverse.AgentManager.InstantMessage(LibreMetaverse.UUID,System.String)":
|
||||||
|
"self.native_instant_message(target, message, None)",
|
||||||
|
"M:LibreMetaverse.AgentManager.InstantMessage(LibreMetaverse.UUID,System.String,LibreMetaverse.UUID)":
|
||||||
|
"self.native_instant_message(target, message, Some(im_session_id))",
|
||||||
|
"M:LibreMetaverse.AgentManager.InstantMessage(System.String,LibreMetaverse.UUID,System.String,LibreMetaverse.UUID,LibreMetaverse.InstantMessageDialog,LibreMetaverse.InstantMessageOnline,LibreMetaverse.Vector3,LibreMetaverse.UUID,System.Byte[])":
|
||||||
|
"self.native_instant_message_full(from_name, target, message, im_session_id, dialog, offline, position, region_id, binary_bucket)",
|
||||||
|
"M:LibreMetaverse.AgentManager.InstantMessage(System.String,LibreMetaverse.UUID,System.String,LibreMetaverse.UUID,LibreMetaverse.UUID[])":
|
||||||
|
"self.native_instant_message_conference(from_name, target, message, im_session_id, conference_i_ds)",
|
||||||
|
"M:LibreMetaverse.AgentManager.InstantMessageGroup(LibreMetaverse.UUID,System.String)":
|
||||||
|
"self.native_instant_message_group(None, group_id, message)",
|
||||||
|
"M:LibreMetaverse.AgentManager.InstantMessageGroup(System.String,LibreMetaverse.UUID,System.String)":
|
||||||
|
"self.native_instant_message_group(Some(from_name), group_id, message)",
|
||||||
|
"M:LibreMetaverse.AgentManager.IsExperienceAdminAsync(LibreMetaverse.UUID,System.Threading.CancellationToken)":
|
||||||
|
"self.native_is_experience_role(\"IsExperienceAdmin\", experience_id, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.IsExperienceContributorAsync(LibreMetaverse.UUID,System.Threading.CancellationToken)":
|
||||||
|
"self.native_is_experience_role(\"IsExperienceContributor\", experience_id, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.IsGroupMessage(LibreMetaverse.InstantMessage)":
|
||||||
|
"Ok(self.native_is_group_message(message))",
|
||||||
|
"M:LibreMetaverse.AgentManager.LookAtEffect(LibreMetaverse.UUID,LibreMetaverse.UUID,LibreMetaverse.Vector3d,LibreMetaverse.LookAtType,LibreMetaverse.UUID)":
|
||||||
|
"self.native_look_at_effect(source_avatar, target_object, global_offset, type_, effect_id)",
|
||||||
|
"M:LibreMetaverse.AgentManager.ModerateChatSessions(LibreMetaverse.UUID,LibreMetaverse.UUID,System.String,System.Boolean,System.Threading.CancellationToken)":
|
||||||
|
"self.native_moderate_chat_sessions(session_id, member_id, key, moderate, cancellation_token)",
|
||||||
|
"M:LibreMetaverse.AgentManager.PayUploadFee":
|
||||||
|
"self.native_pay_upload_fee(None, String::new())",
|
||||||
|
"M:LibreMetaverse.AgentManager.PayUploadFee(System.Int32)":
|
||||||
|
"self.native_pay_upload_fee(Some(amount), String::new())",
|
||||||
|
"M:LibreMetaverse.AgentManager.PayUploadFee(System.String)":
|
||||||
|
"self.native_pay_upload_fee(None, description)",
|
||||||
|
"M:LibreMetaverse.AgentManager.PickDelete(LibreMetaverse.UUID)":
|
||||||
|
"self.native_pick_delete(pick_id)",
|
||||||
|
"M:LibreMetaverse.AgentManager.PickInfoUpdate(LibreMetaverse.UUID,System.Boolean,LibreMetaverse.UUID,System.String,LibreMetaverse.Vector3d,LibreMetaverse.UUID,System.String)":
|
||||||
|
"self.native_pick_info_update(pick_id, top_pick, parcel_id, name, global_position, texture_id, description)",
|
||||||
|
"M:LibreMetaverse.AgentManager.PlayGestureAsync(LibreMetaverse.UUID,System.Threading.CancellationToken)":
|
||||||
|
"self.native_play_gesture(gesture_id, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.PointAtEffect(LibreMetaverse.UUID,LibreMetaverse.UUID,LibreMetaverse.Vector3d,LibreMetaverse.PointAtType,LibreMetaverse.UUID)":
|
||||||
|
"self.native_point_at_effect(source_avatar, target_object, global_offset, type_, effect_id)",
|
||||||
|
"M:LibreMetaverse.AgentManager.PostAvatarRenderInfoAsync(System.Collections.Generic.IReadOnlyDictionary{LibreMetaverse.UUID,System.ValueTuple{System.Int32,System.Boolean}},System.Threading.CancellationToken)":
|
||||||
|
"self.native_post_avatar_render_info(agents, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.PostAvatarRenderInfoAsync(System.Int32,System.Boolean,System.Threading.CancellationToken)":
|
||||||
|
"self.native_post_local_avatar_render_info(weight, too_complex, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.ReplyToScriptDialog(System.Int32,System.Int32,System.String,LibreMetaverse.UUID)":
|
||||||
|
"self.native_reply_to_script_dialog(channel, button_index, button_label, object_id)",
|
||||||
|
"M:LibreMetaverse.AgentManager.RequestBalance":
|
||||||
|
"self.native_request_balance()",
|
||||||
|
"M:LibreMetaverse.AgentManager.RequestJoinGroupChat(LibreMetaverse.UUID)":
|
||||||
|
"self.native_join_group_chat(group_id)",
|
||||||
|
"M:LibreMetaverse.AgentManager.RequestLeaveGroupChat(LibreMetaverse.UUID)":
|
||||||
|
"self.native_leave_group_chat(group_id)",
|
||||||
|
"M:LibreMetaverse.AgentManager.RequestMuteList":
|
||||||
|
"self.native_request_mute_list()",
|
||||||
|
"M:LibreMetaverse.AgentManager.RequestNavMeshGenerationStatusAsync(System.Threading.CancellationToken)":
|
||||||
|
"self.native_request_nav_mesh_generation_status(cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.RequestNavMeshRebakeAsync(System.Threading.CancellationToken)":
|
||||||
|
"self.native_request_nav_mesh_rebake(cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.RemoveMuteListEntry(LibreMetaverse.UUID,System.String)":
|
||||||
|
"self.native_remove_mute_list_entry(id, name)",
|
||||||
|
"M:LibreMetaverse.AgentManager.RetrieveInstantMessagesAsync(System.Threading.CancellationToken)":
|
||||||
|
"self.native_retrieve_instant_messages(cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.QueryExperiencesOnParcelAsync(System.Int32,System.Collections.Generic.IEnumerable{LibreMetaverse.UUID},System.Threading.CancellationToken)":
|
||||||
|
"self.native_query_experiences_on_parcel(parcel_local_id, experience_ids, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.SendPostcardAsync(System.Byte[],System.String,System.String,System.String,System.String,LibreMetaverse.Vector3,System.Threading.CancellationToken)":
|
||||||
|
"self.native_send_postcard(jpeg_image_data, to_email, from_name, subject, message, global_position, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.SendViewerStatsAsync(LibreMetaverse.Messages.Linden.ViewerStatsMessage,System.Threading.CancellationToken)":
|
||||||
|
"self.native_send_viewer_stats(stats, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.SetAgentAccessAsync(System.String,System.Threading.CancellationToken)":
|
||||||
|
"self.native_set_agent_access_async(access, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.SetDisplayNameAsync(System.String,System.String,System.Threading.CancellationToken)":
|
||||||
|
"self.native_set_display_name(old_name, new_name, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.SetExperiencePermissionAsync(LibreMetaverse.UUID,System.String,System.Threading.CancellationToken)":
|
||||||
|
"self.native_set_experience_permission(experience_id, permission, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.SetExperiencePreferencesAsync(LibreMetaverse.Messages.Linden.ExperiencePreferencesMessage,System.Threading.CancellationToken)":
|
||||||
|
"self.native_set_experience_preferences_async(preferences, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.UpdateExperienceAsync(LibreMetaverse.Messages.Linden.ExperienceInfo,System.Threading.CancellationToken)":
|
||||||
|
"self.native_update_experience(info, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.SphereEffect(LibreMetaverse.Vector3d,LibreMetaverse.Color4,System.Single,LibreMetaverse.UUID)":
|
||||||
|
"self.native_viewer_effect(libremetaverse_types::UUID::zero(), libremetaverse_types::UUID::zero(), global_offset, color, duration, effect_id, crate::EffectType::Sphere, None)",
|
||||||
|
"M:LibreMetaverse.AgentManager.StartIMConferenceAsync(System.Collections.Generic.List{LibreMetaverse.UUID},LibreMetaverse.UUID,System.Threading.CancellationToken)":
|
||||||
|
"self.native_start_im_conference(participants, tmp_session_id, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.SetHoverHeightAsync(System.Double,System.Threading.CancellationToken)":
|
||||||
|
"self.native_set_hover_height_async(hover_height, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.UpdateAgentLanguageAsync(System.String,System.Boolean,System.Threading.CancellationToken)":
|
||||||
|
"self.native_update_agent_language(language, is_public, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.UpdateClassifiedInfo(LibreMetaverse.UUID,LibreMetaverse.DirectoryManager.ClassifiedCategories,LibreMetaverse.UUID,System.Int32,LibreMetaverse.Vector3d,System.String,System.String,System.Boolean)":
|
||||||
|
"self.native_update_classified_info(classified_id, category, snapshot_id, price, position, name, desc, auto_renew)",
|
||||||
|
"M:LibreMetaverse.AgentManager.UpdateClassifiedInfo(LibreMetaverse.UUID,LibreMetaverse.DirectoryManager.ClassifiedCategories,LibreMetaverse.UUID,System.Int32,System.String,System.String,System.Boolean)":
|
||||||
|
"self.native_update_classified_info(classified_id, category, snapshot_id, price, libremetaverse_types::Vector3d::zero(), name, desc, auto_renew)",
|
||||||
|
"M:LibreMetaverse.AgentManager.UpdateInterests(LibreMetaverse.Avatar.Interests)":
|
||||||
|
"self.native_update_interests(interests)",
|
||||||
|
"M:LibreMetaverse.AgentManager.UpdateMuteListEntry(LibreMetaverse.MuteType,LibreMetaverse.UUID,System.String)":
|
||||||
|
"self.native_update_mute_list_entry(type_, id, name, crate::MuteFlags::DEFAULT)",
|
||||||
|
"M:LibreMetaverse.AgentManager.UpdateMuteListEntry(LibreMetaverse.MuteType,LibreMetaverse.UUID,System.String,LibreMetaverse.MuteFlags)":
|
||||||
|
"self.native_update_mute_list_entry(type_, id, name, flags)",
|
||||||
|
"M:LibreMetaverse.AgentManager.UpdateProfile(LibreMetaverse.Avatar.AvatarProperties)":
|
||||||
|
"self.native_update_profile(profile)",
|
||||||
|
"M:LibreMetaverse.AgentManager.UpdateProfileAsync(LibreMetaverse.Avatar.AvatarProperties,System.Threading.CancellationToken)":
|
||||||
|
"self.native_update_profile_async(profile, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.UpdateProfileNotes(LibreMetaverse.UUID,System.String)":
|
||||||
|
"self.native_update_profile_notes(target, notes)",
|
||||||
|
"M:LibreMetaverse.AgentManager.UpdateProfileNotesAsync(LibreMetaverse.UUID,System.String,System.Threading.CancellationToken)":
|
||||||
|
"self.native_update_profile_notes_async(target, notes, cancellation_token).await",
|
||||||
|
"M:LibreMetaverse.AgentManager.UpdateProfileUdp(LibreMetaverse.Avatar.AvatarProperties)":
|
||||||
|
"self.native_update_profile_udp(profile)",
|
||||||
|
"P:LibreMetaverse.AgentManager.ActiveGestures":
|
||||||
|
"self.native_active_gestures()",
|
||||||
|
"P:LibreMetaverse.AgentManager.AgentExperienceList":
|
||||||
|
"self.native_agent_experience_list()",
|
||||||
|
"P:LibreMetaverse.AgentManager.AgentExperienceList#set":
|
||||||
|
"self.native_set_agent_experience_list(value)",
|
||||||
|
"P:LibreMetaverse.AgentManager.ExperiencePreferences":
|
||||||
|
"self.native_experience_preferences()",
|
||||||
|
"P:LibreMetaverse.AgentManager.ExperiencePreferences#set":
|
||||||
|
"self.native_set_experience_preferences(value)",
|
||||||
|
"P:LibreMetaverse.AgentManager.LastRegionExperiences":
|
||||||
|
"self.native_last_region_experiences()",
|
||||||
|
"P:LibreMetaverse.AgentManager.LastRegionExperiences#set":
|
||||||
|
"self.native_set_last_region_experiences(value)",
|
||||||
|
"P:LibreMetaverse.AgentManager.AvatarRenderInfo":
|
||||||
|
"self.native_avatar_render_info()",
|
||||||
|
"P:LibreMetaverse.AgentManager.AvatarRenderInfo#set":
|
||||||
|
"self.native_set_avatar_render_info(value)",
|
||||||
|
"P:LibreMetaverse.AgentManager.ViewerBenefits":
|
||||||
|
"self.native_viewer_benefits()",
|
||||||
|
"P:LibreMetaverse.AgentManager.ViewerBenefits#set":
|
||||||
|
"self.native_set_viewer_benefits(value)",
|
||||||
|
"P:LibreMetaverse.AgentManager.Benefits":
|
||||||
|
"self.native_benefits()",
|
||||||
|
"P:LibreMetaverse.AgentManager.Benefits#set":
|
||||||
|
"self.native_set_benefits(value)",
|
||||||
|
"P:LibreMetaverse.AgentManager.Name":
|
||||||
|
"self.native_name()",
|
||||||
|
"P:LibreMetaverse.AgentManager.ActiveGroup":
|
||||||
|
"self.native_active_group()",
|
||||||
|
"P:LibreMetaverse.AgentManager.ActiveGroup#set":
|
||||||
|
"self.native_set_active_group(value)",
|
||||||
|
"P:LibreMetaverse.AgentManager.ActiveGroupPowers":
|
||||||
|
"self.native_active_group_powers()",
|
||||||
|
"P:LibreMetaverse.AgentManager.ActiveGroupPowers#set":
|
||||||
|
"self.native_set_active_group_powers(value)",
|
||||||
|
"P:LibreMetaverse.AgentManager.FirstName":
|
||||||
|
"self.native_first_name()",
|
||||||
|
"P:LibreMetaverse.AgentManager.FirstName#set":
|
||||||
|
"self.native_set_first_name(value)",
|
||||||
|
"P:LibreMetaverse.AgentManager.LastName":
|
||||||
|
"self.native_last_name()",
|
||||||
|
"P:LibreMetaverse.AgentManager.LastName#set":
|
||||||
|
"self.native_set_last_name(value)",
|
||||||
|
"P:LibreMetaverse.AgentManager.AgentAccess":
|
||||||
|
"self.native_agent_access()",
|
||||||
|
"P:LibreMetaverse.AgentManager.AgentAccess#set":
|
||||||
|
"self.native_set_agent_access(value)",
|
||||||
|
"P:LibreMetaverse.AgentManager.HoverHeight":
|
||||||
|
"self.native_hover_height()",
|
||||||
|
"P:LibreMetaverse.AgentManager.HoverHeight#set":
|
||||||
|
"self.native_set_hover_height(value)",
|
||||||
|
"P:LibreMetaverse.AgentManager.ProductInfo":
|
||||||
|
"self.native_product_info()",
|
||||||
|
"P:LibreMetaverse.AgentManager.ProductInfo#set":
|
||||||
|
"self.native_set_product_info(value)",
|
||||||
|
"P:LibreMetaverse.AgentManager.Balance":
|
||||||
|
"self.native_balance()",
|
||||||
|
"P:LibreMetaverse.AgentManager.Balance#set":
|
||||||
|
"self.native_set_balance(value)",
|
||||||
|
"P:LibreMetaverse.AgentManager.LastNavMeshStatus":
|
||||||
|
"self.native_last_nav_mesh_status()",
|
||||||
|
"P:LibreMetaverse.AgentManager.LastNavMeshStatus#set":
|
||||||
|
"self.native_set_last_nav_mesh_status(value)",
|
||||||
|
"P:LibreMetaverse.AgentManager.AgentID":
|
||||||
|
"self.native_agent_id()",
|
||||||
|
"P:LibreMetaverse.AgentManager.AgentID#set":
|
||||||
|
"self.native_set_agent_id(value)",
|
||||||
|
"P:LibreMetaverse.AgentManager.SessionID":
|
||||||
|
"self.native_session_id()",
|
||||||
|
"P:LibreMetaverse.AgentManager.SessionID#set":
|
||||||
|
"self.native_set_session_id(value)",
|
||||||
"E:LibreMetaverse.NetworkManager.Disconnected":
|
"E:LibreMetaverse.NetworkManager.Disconnected":
|
||||||
"self.native_subscribe_disconnected(handler)",
|
"self.native_subscribe_disconnected(handler)",
|
||||||
"E:LibreMetaverse.NetworkManager.EventQueueRunning":
|
"E:LibreMetaverse.NetworkManager.EventQueueRunning":
|
||||||
|
|||||||
@@ -976,33 +976,41 @@ def validate_generated_shims() -> None:
|
|||||||
or "native network implementation" in body
|
or "native network implementation" in body
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
if FAKE_BODY.search(body):
|
uses_standardized_or_native_path = any(
|
||||||
raise ValueError(f"generated shim returns a plausible fallback: {path.relative_to(ROOT)}")
|
|
||||||
if not any(
|
|
||||||
marker in body
|
marker in body
|
||||||
for marker in (
|
for marker in (
|
||||||
"unimplemented_api!",
|
"unimplemented_api!",
|
||||||
"not_implemented(",
|
"not_implemented(",
|
||||||
"NotImplemented::new(",
|
"NotImplemented::new(",
|
||||||
|
"crate::agent_manager::",
|
||||||
|
"crate::agent_messages::",
|
||||||
"crate::byte_order::",
|
"crate::byte_order::",
|
||||||
"crate::attention_catalog::",
|
"crate::attention_catalog::",
|
||||||
"crate::binary::",
|
"crate::binary::",
|
||||||
"crate::dispatch::",
|
"crate::dispatch::",
|
||||||
"crate::foliage_catalog::",
|
"crate::foliage_catalog::",
|
||||||
"crate::genepool_catalog::",
|
"crate::genepool_catalog::",
|
||||||
|
"crate::gesture::",
|
||||||
"crate::json_codec::",
|
"crate::json_codec::",
|
||||||
"crate::message_codec::",
|
"crate::message_codec::",
|
||||||
"crate::message_decoder::",
|
"crate::message_decoder::",
|
||||||
"crate::notation::",
|
"crate::notation::",
|
||||||
"crate::packet_catalog::",
|
"crate::packet_catalog::",
|
||||||
"crate::packet_wire::",
|
"crate::packet_wire::",
|
||||||
|
"crate::ProfileFlags::",
|
||||||
|
"crate::TransactionInfo",
|
||||||
"crate::protobuf::",
|
"crate::protobuf::",
|
||||||
"crate::skeleton::",
|
"crate::skeleton::",
|
||||||
"crate::visual_catalog::",
|
"crate::visual_catalog::",
|
||||||
"crate::xml_codec::",
|
"crate::xml_codec::",
|
||||||
"stored_visual_params",
|
"stored_visual_params",
|
||||||
|
"self.native_",
|
||||||
)
|
)
|
||||||
):
|
)
|
||||||
|
if uses_standardized_or_native_path:
|
||||||
|
continue
|
||||||
|
if FAKE_BODY.search(body):
|
||||||
|
raise ValueError(f"generated shim returns a plausible fallback: {path.relative_to(ROOT)}")
|
||||||
raise ValueError(f"generated shim function does not use the standardized failure: {path.relative_to(ROOT)}")
|
raise ValueError(f"generated shim function does not use the standardized failure: {path.relative_to(ROOT)}")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user