Implement appearance and current outfit services (#65)
Some checks failed
Native code generation / deterministic (push) Failing after 2m19s
Imaging and meshing gate / native (push) Failing after 1m26s
Native Rust workspace compile / compile (push) Failing after 2m11s

This commit is contained in:
2026-08-10 08:58:29 +00:00
parent 52f62d8c39
commit a9927fcfcb
13 changed files with 2900 additions and 242 deletions

View File

@@ -0,0 +1,964 @@
//! Native wearable and attachment state for `AppearanceManager`.
use crate::client_core::ClientWeakHandle;
use crate::packet_catalog::PacketType;
use crate::packets::{
AgentIsNowWearingPacket, AgentIsNowWearingPacketWearableDataBlock,
DetachAttachmentIntoInvPacket, RezSingleAttachmentFromInvPacket,
};
use crate::{
AgentWearablesReplyEventArgs, AppearanceManagerWearableData, AppearanceSetEventArgs,
GridClient, InventoryBase, InventoryFolder, InventoryItem, InventoryObject,
InventoryObjectClass, InventoryWearable, Permissions, PrimitiveTextureEntry,
appearance::CurrentOutfitFolder,
};
use libremetaverse_types::compat::{CancellationToken, EventHandler, Subscription};
use libremetaverse_types::{
AssetType, AttachmentPoint, Error, FolderType, InventoryType, MultiValueDictionary, UUID,
WearableType,
};
use std::collections::{HashMap, HashSet, VecDeque};
use std::fmt;
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering};
use std::sync::{Arc, Mutex, RwLock};
const ATTACHMENT_ADD: u8 = 0x80;
const MAX_WEARABLE_LAYERS: usize = 60;
const MAX_FOLDER_TRAVERSAL: usize = 256;
fn mutex<T>(lock: &Mutex<T>) -> std::sync::MutexGuard<'_, T> {
lock.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
fn read<T>(lock: &RwLock<T>) -> std::sync::RwLockReadGuard<'_, T> {
lock.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
fn write<T>(lock: &RwLock<T>) -> std::sync::RwLockWriteGuard<'_, T> {
lock.write()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
fn check_cancelled(token: Option<&CancellationToken>) -> Result<(), Error> {
token.map_or(Ok(()), CancellationToken::throw_if_cancellation_requested)
}
struct EventRegistry<T> {
next_id: AtomicU64,
handlers: Arc<Mutex<HashMap<u64, EventHandler<T>>>>,
}
impl<T> Default for EventRegistry<T> {
fn default() -> Self {
Self {
next_id: AtomicU64::new(1),
handlers: Arc::new(Mutex::new(HashMap::new())),
}
}
}
impl<T: 'static> EventRegistry<T> {
fn subscribe(&self, handler: EventHandler<T>) -> Subscription {
let id = self.next_id.fetch_add(1, Ordering::Relaxed);
mutex(&self.handlers).insert(id, handler);
let handlers = Arc::downgrade(&self.handlers);
Subscription::new(move || {
if let Some(handlers) = handlers.upgrade() {
mutex(&handlers).remove(&id);
}
})
}
fn emit_with(&self, mut event: impl FnMut() -> T) {
let handlers: Vec<_> = mutex(&self.handlers).values().cloned().collect();
for handler in handlers {
handler(event());
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct WearableRecord {
item_id: UUID,
asset_id: UUID,
asset_type: AssetType,
wearable_type: WearableType,
}
impl WearableRecord {
fn from_item(item: &InventoryItem) -> Option<Self> {
if !matches!(item.asset_type(), AssetType::Bodypart | AssetType::Clothing) {
return None;
}
let wearable = InventoryWearable { base: item.clone() };
Some(Self {
item_id: item.base.uuid(),
asset_id: item.asset_uuid(),
asset_type: item.asset_type(),
wearable_type: wearable.wearable_type(),
})
}
fn public(&self) -> AppearanceManagerWearableData {
AppearanceManagerWearableData {
asset: None,
asset_id: self.asset_id,
asset_type: self.asset_type,
item_id: self.item_id,
wearable_type: self.wearable_type,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct AttachmentRecord {
item: InventoryItem,
point: AttachmentPoint,
}
#[derive(Default)]
struct AppearanceEvents {
agent_wearables_reply: EventRegistry<AgentWearablesReplyEventArgs>,
appearance_set: EventRegistry<AppearanceSetEventArgs>,
}
pub(crate) struct AppearanceManagerInner {
client: ClientWeakHandle,
wearables: RwLock<Vec<WearableRecord>>,
attachments: RwLock<Vec<AttachmentRecord>>,
last_cof_version: AtomicI32,
busy: AtomicBool,
disposed: AtomicBool,
events: AppearanceEvents,
}
/// Shared native manager. Public texture fields remain on the compatibility
/// wrapper; wearable and attachment state is shared by every client accessor.
pub struct AppearanceManager {
pub my_textures: PrimitiveTextureEntry,
pub my_visual_parameters: Vec<u8>,
inner: Arc<AppearanceManagerInner>,
}
impl Clone for AppearanceManager {
fn clone(&self) -> Self {
Self::native_from_inner(Arc::clone(&self.inner))
}
}
impl fmt::Debug for AppearanceManager {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("AppearanceManager")
.field("wearables", &read(&self.inner.wearables).len())
.field("attachments", &read(&self.inner.attachments).len())
.field("busy", &self.native_manager_busy())
.finish_non_exhaustive()
}
}
struct BusyGuard<'a>(&'a AtomicBool);
impl Drop for BusyGuard<'_> {
fn drop(&mut self) {
self.0.store(false, Ordering::Release);
}
}
impl AppearanceManager {
pub(crate) fn native_new(client: Option<Arc<GridClient>>) -> Result<Self, Error> {
let client = client.ok_or(Error::ArgumentNull)?;
Ok(Self::native_from_inner(Arc::new(AppearanceManagerInner {
client: client.native_weak_handle(),
wearables: RwLock::new(Vec::new()),
attachments: RwLock::new(Vec::new()),
last_cof_version: AtomicI32::new(-1),
busy: AtomicBool::new(false),
disposed: AtomicBool::new(false),
events: AppearanceEvents::default(),
})))
}
pub(crate) fn native_from_inner(inner: Arc<AppearanceManagerInner>) -> Self {
Self {
my_textures: PrimitiveTextureEntry {
default_texture: None,
face_textures: Vec::new(),
},
my_visual_parameters: Vec::new(),
inner,
}
}
pub(crate) fn native_inner(&self) -> Arc<AppearanceManagerInner> {
Arc::clone(&self.inner)
}
fn client(&self) -> Result<GridClient, Error> {
if self.inner.disposed.load(Ordering::Acquire) {
return Err(Error::InvalidOperation);
}
self.inner.client.upgrade().ok_or(Error::InvalidOperation)
}
fn begin(&self) -> Result<BusyGuard<'_>, Error> {
self.client()?;
if self
.inner
.busy
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
.is_err()
{
return Err(Error::InvalidOperation);
}
Ok(BusyGuard(&self.inner.busy))
}
pub(crate) fn native_subscribe_agent_wearables_reply(
&self,
handler: EventHandler<AgentWearablesReplyEventArgs>,
) -> Subscription {
self.inner.events.agent_wearables_reply.subscribe(handler)
}
pub(crate) fn native_subscribe_appearance_set(
&self,
handler: EventHandler<AppearanceSetEventArgs>,
) -> Subscription {
self.inner.events.appearance_set.subscribe(handler)
}
#[allow(clippy::unnecessary_wraps)] // The mapped Dispose API is fallible.
pub(crate) fn native_dispose(&self) -> Result<(), Error> {
self.inner.disposed.store(true, Ordering::Release);
write(&self.inner.wearables).clear();
write(&self.inner.attachments).clear();
Ok(())
}
pub(crate) fn native_manager_busy(&self) -> bool {
self.inner.busy.load(Ordering::Acquire)
}
pub(crate) fn native_last_cof_version(&self) -> i32 {
self.inner.last_cof_version.load(Ordering::Acquire)
}
#[allow(clippy::unnecessary_wraps)] // The mapped method returns Result<(), Error>.
pub(crate) fn native_update_cof_version(&self, version: i32) -> Result<(), Error> {
self.inner
.last_cof_version
.fetch_max(version, Ordering::AcqRel);
Ok(())
}
pub(crate) fn native_add_to_outfit(
&self,
items: Vec<InventoryItem>,
replace: bool,
) -> Result<(), Error> {
let busy = self.begin()?;
let mut next = read(&self.inner.wearables).clone();
apply_wearables(&mut next, items, replace)?;
self.send_wearables(&next)?;
*write(&self.inner.wearables) = next;
drop(busy);
self.inner
.events
.appearance_set
.emit_with(|| AppearanceSetEventArgs);
self.inner
.events
.agent_wearables_reply
.emit_with(|| AgentWearablesReplyEventArgs);
Ok(())
}
pub(crate) fn native_remove_from_outfit(&self, items: Vec<InventoryItem>) -> Result<(), Error> {
let busy = self.begin()?;
let ids: HashSet<_> = items.into_iter().map(|item| item.base.uuid()).collect();
let mut next = read(&self.inner.wearables).clone();
next.retain(|wearable| !ids.contains(&wearable.item_id));
self.send_wearables(&next)?;
*write(&self.inner.wearables) = next;
drop(busy);
self.inner
.events
.appearance_set
.emit_with(|| AppearanceSetEventArgs);
self.inner
.events
.agent_wearables_reply
.emit_with(|| AgentWearablesReplyEventArgs);
Ok(())
}
#[allow(clippy::unused_async)] // Preserves the mapped asynchronous API boundary.
pub(crate) async fn native_replace_outfit(
&self,
items: Vec<InventoryItem>,
safe: bool,
) -> Result<(), Error> {
let busy = self.begin()?;
let mut next = if safe {
read(&self.inner.wearables)
.iter()
.filter(|wearable| wearable.asset_type == AssetType::Bodypart)
.cloned()
.collect()
} else {
Vec::new()
};
apply_wearables(&mut next, items, false)?;
self.send_wearables(&next)?;
*write(&self.inner.wearables) = next;
drop(busy);
self.inner
.events
.appearance_set
.emit_with(|| AppearanceSetEventArgs);
self.inner
.events
.agent_wearables_reply
.emit_with(|| AgentWearablesReplyEventArgs);
Ok(())
}
pub(crate) async fn native_wear_outfit(
&self,
values: Vec<InventoryBase>,
replace: bool,
) -> Result<(), Error> {
let client = self.client()?;
let store = client
.native_inventory()?
.native_store()
.ok_or(Error::InvalidOperation)?;
let items: Vec<_> = values
.into_iter()
.filter_map(|value| store.native_item_value(value.uuid()))
.collect();
if replace {
self.native_replace_outfit(items, false).await
} else {
self.native_add_to_outfit(items, false)
}
}
pub(crate) fn native_get_wearables(
&self,
) -> Result<Box<dyn Iterator<Item = AppearanceManagerWearableData>>, Error> {
self.client()?;
let values: Vec<_> = read(&self.inner.wearables)
.iter()
.map(WearableRecord::public)
.collect();
Ok(Box::new(values.into_iter()))
}
pub(crate) fn native_get_wearable_assets(
&self,
wearable_type: WearableType,
) -> Result<Box<dyn Iterator<Item = UUID>>, Error> {
self.client()?;
let values: Vec<_> = read(&self.inner.wearables)
.iter()
.filter(|wearable| wearable.wearable_type == wearable_type)
.map(|wearable| wearable.asset_id)
.collect();
Ok(Box::new(values.into_iter()))
}
pub(crate) fn native_get_wearables_by_type(
&self,
) -> Result<MultiValueDictionary<WearableType, AppearanceManagerWearableData>, Error> {
self.client()?;
let result = MultiValueDictionary::new_with_constructor()?;
for wearable in read(&self.inner.wearables).iter() {
result.add(wearable.wearable_type, Some(wearable.public()))?;
}
Ok(result)
}
pub(crate) fn native_is_item_worn(&self, item_id: UUID) -> Result<WearableType, Error> {
self.client()?;
Ok(read(&self.inner.wearables)
.iter()
.find(|wearable| wearable.item_id == item_id)
.map_or(WearableType::Invalid, |wearable| wearable.wearable_type))
}
#[allow(clippy::unused_async)] // Preserves the mapped asynchronous API boundary.
pub(crate) async fn native_send_outfit(
&self,
cancellation_token: Option<CancellationToken>,
) -> Result<(), Error> {
check_cancelled(cancellation_token.as_ref())?;
let busy = self.begin()?;
let snapshot = read(&self.inner.wearables).clone();
check_cancelled(cancellation_token.as_ref())?;
self.send_wearables(&snapshot)?;
drop(busy);
self.inner
.events
.appearance_set
.emit_with(|| AppearanceSetEventArgs);
Ok(())
}
fn send_wearables(&self, wearables: &[WearableRecord]) -> Result<(), Error> {
let client = self.client()?;
let network = client.native_network()?;
let mut packet = AgentIsNowWearingPacket::new_with_constructor()?;
packet.agent_data.agent_id = network.native_agent_id();
packet.agent_data.session_id = network.native_session_id();
packet.wearable_data = wearables
.iter()
.map(|wearable| {
let mut block = AgentIsNowWearingPacketWearableDataBlock::new_with_constructor()?;
block.item_id = wearable.item_id;
block.wearable_type = wearable.wearable_type as u8;
Ok(block)
})
.collect::<Result<Vec<_>, Error>>()?;
send_generated(
&network,
PacketType::AgentIsNowWearing,
packet.to_bytes_with_method()?,
)?;
Ok(())
}
pub(crate) fn native_attach(
&self,
item: InventoryItem,
point: AttachmentPoint,
replace: bool,
) -> Result<(), Error> {
let busy = self.begin()?;
let name = item.base.name();
let description = item.description();
let permissions = item.permissions();
self.send_attach_packet(
item.base.uuid(),
item.base.owner_id(),
&name,
&description,
permissions,
item.flags(),
point,
replace,
)?;
self.record_attachment(item, point, replace);
drop(busy);
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn native_attach_fields(
&self,
item_id: UUID,
owner_id: UUID,
name: String,
description: String,
permissions: Permissions,
item_flags: u32,
point: AttachmentPoint,
replace: bool,
) -> Result<(), Error> {
let busy = self.begin()?;
self.send_attach_packet(
item_id,
owner_id,
&name,
&description,
permissions,
item_flags,
point,
replace,
)?;
let mut item = InventoryItem::new_with_inventory_type_uuid(InventoryType::OBJECT, item_id)?;
item.base.set_owner_id(owner_id);
item.base.set_name(name);
item.set_description(description);
item.set_permissions(permissions);
item.set_flags(item_flags);
item.set_asset_type(AssetType::Object);
self.record_attachment(item, point, replace);
drop(busy);
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn send_attach_packet(
&self,
item_id: UUID,
owner_id: UUID,
name: &str,
description: &str,
permissions: Permissions,
item_flags: u32,
point: AttachmentPoint,
replace: bool,
) -> Result<(), Error> {
let client = self.client()?;
let network = client.native_network()?;
send_generated(
&network,
PacketType::RezSingleAttachmentFromInv,
attachment_packet_bytes(
network.native_agent_id(),
network.native_session_id(),
item_id,
owner_id,
name,
description,
permissions,
item_flags,
point,
replace,
)?,
)
}
fn record_attachment(&self, item: InventoryItem, point: AttachmentPoint, replace: bool) {
let mut attachments = write(&self.inner.attachments);
if replace && point != AttachmentPoint::Default {
attachments.retain(|existing| existing.point != point);
}
attachments.retain(|existing| existing.item.base.uuid() != item.base.uuid());
attachments.push(AttachmentRecord { item, point });
}
pub(crate) fn native_add_attachments(
&self,
items: Vec<InventoryItem>,
remove_existing_first: bool,
replace: bool,
) -> Result<(), Error> {
let busy = self.begin()?;
let existing = read(&self.inner.attachments).clone();
if remove_existing_first {
for attachment in &existing {
self.send_detach_packet(attachment.item.base.uuid())?;
write(&self.inner.attachments)
.retain(|candidate| candidate.item.base.uuid() != attachment.item.base.uuid());
}
}
for item in items {
let object = InventoryObject { base: item.clone() };
let point = object.attach_point();
let name = item.base.name();
let description = item.description();
self.send_attach_packet(
item.base.uuid(),
item.base.owner_id(),
&name,
&description,
item.permissions(),
item.flags(),
point,
replace,
)?;
self.record_attachment(item, point, replace);
}
drop(busy);
Ok(())
}
pub(crate) fn native_detach(&self, item_id: UUID) -> Result<(), Error> {
let busy = self.begin()?;
self.send_detach_packet(item_id)?;
write(&self.inner.attachments).retain(|attachment| attachment.item.base.uuid() != item_id);
drop(busy);
Ok(())
}
fn send_detach_packet(&self, item_id: UUID) -> Result<(), Error> {
let client = self.client()?;
let network = client.native_network()?;
let mut packet = DetachAttachmentIntoInvPacket::new_with_constructor()?;
packet.object_data.agent_id = network.native_agent_id();
packet.object_data.item_id = item_id;
send_generated(
&network,
PacketType::DetachAttachmentIntoInv,
packet.to_bytes_with_method()?,
)
}
pub(crate) fn native_get_attachments(
&self,
) -> Result<Box<dyn Iterator<Item = InventoryItem>>, Error> {
self.client()?;
let values: Vec<_> = read(&self.inner.attachments)
.iter()
.map(|attachment| attachment.item.clone())
.collect();
Ok(Box::new(values.into_iter()))
}
#[allow(clippy::unused_async)] // Preserves the mapped asynchronous API boundary.
pub(crate) async fn native_get_attachments_by_point(
&self,
cancellation_token: Option<CancellationToken>,
) -> Result<MultiValueDictionary<AttachmentPoint, InventoryItem>, Error> {
check_cancelled(cancellation_token.as_ref())?;
self.client()?;
let result = MultiValueDictionary::new_with_constructor()?;
for attachment in read(&self.inner.attachments).iter() {
result.add(attachment.point, Some(attachment.item.clone()))?;
}
Ok(result)
}
pub(crate) fn native_get_attachments_by_item(
&self,
) -> Result<HashMap<InventoryItem, AttachmentPoint>, Error> {
self.client()?;
Ok(read(&self.inner.attachments)
.iter()
.map(|attachment| (attachment.item.clone(), attachment.point))
.collect())
}
pub(crate) fn native_get_attachments_by_id(
&self,
) -> Result<HashMap<UUID, AttachmentPoint>, Error> {
self.client()?;
Ok(read(&self.inner.attachments)
.iter()
.map(|attachment| (attachment.item.base.uuid(), attachment.point))
.collect())
}
pub(crate) fn native_is_item_attached(&self, item_id: UUID) -> bool {
read(&self.inner.attachments)
.iter()
.any(|attachment| attachment.item.base.uuid() == item_id)
}
pub(crate) fn native_attachments_at(&self, point: AttachmentPoint) -> Vec<InventoryItem> {
read(&self.inner.attachments)
.iter()
.filter(|attachment| attachment.point == point)
.map(|attachment| attachment.item.clone())
.collect()
}
pub(crate) fn native_attachment_point(&self, item_id: UUID) -> Option<AttachmentPoint> {
read(&self.inner.attachments)
.iter()
.find(|attachment| attachment.item.base.uuid() == item_id)
.map(|attachment| attachment.point)
}
#[allow(clippy::unused_async)] // Preserves the mapped asynchronous API boundary.
pub(crate) async fn native_get_current_outfit_folder(
&self,
cancellation_token: Option<CancellationToken>,
) -> Result<Option<InventoryFolder>, Error> {
check_cancelled(cancellation_token.as_ref())?;
let client = self.client()?;
let Some(store) = client.native_inventory()?.native_store() else {
return Ok(None);
};
let Some(id) = store.find_folder_for_type(FolderType::CurrentOutfit) else {
return Ok(None);
};
store.get_value_or_default_with_uuid_a7c63fbe(id)
}
#[allow(clippy::unused_async)] // Preserves the mapped asynchronous API boundary.
pub(crate) async fn native_get_folder_wearables(
&self,
folder: UUID,
cancellation_token: Option<CancellationToken>,
) -> Result<(bool, Vec<InventoryWearable>, Vec<InventoryItem>), Error> {
check_cancelled(cancellation_token.as_ref())?;
let client = self.client()?;
let Some(store) = client.native_inventory()?.native_store() else {
return Ok((false, Vec::new(), Vec::new()));
};
let mut queue = VecDeque::from([folder]);
let mut visited = HashSet::new();
let mut scheduled = HashSet::from([folder]);
let mut wearables = Vec::new();
let mut attachments = Vec::new();
while let Some(folder_id) = queue.pop_front() {
check_cancelled(cancellation_token.as_ref())?;
if !visited.insert(folder_id) {
continue;
}
let Ok(contents) = store.get_contents_with_uuid(folder_id) else {
return Ok((false, wearables, attachments));
};
for value in contents {
if let Some(child) = value.as_any().downcast_ref::<InventoryFolder>() {
let child_id = child.base.uuid();
if scheduled.insert(child_id) {
if scheduled.len() > MAX_FOLDER_TRAVERSAL {
return Ok((false, wearables, attachments));
}
queue.push_back(child_id);
}
continue;
}
let Some(item) = inventory_item(value.as_ref()) else {
continue;
};
if matches!(item.asset_type(), AssetType::Bodypart | AssetType::Clothing) {
wearables.push(InventoryWearable { base: item });
} else if matches!(item.asset_type(), AssetType::Object | AssetType::Mesh) {
attachments.push(item);
}
}
}
Ok((true, wearables, attachments))
}
pub(crate) async fn native_request_agent_worn(
&self,
cancellation_token: Option<CancellationToken>,
) -> Result<Vec<InventoryBase>, Error> {
let client = self.client()?;
let cof = CurrentOutfitFolder::new(Some(Arc::new(client)))?;
let links = cof
.get_current_outfit_links(cancellation_token.clone())
.await?;
check_cancelled(cancellation_token.as_ref())?;
Ok(links.into_iter().map(|item| item.base).collect())
}
}
impl PartialEq for AppearanceManagerWearableData {
fn eq(&self, other: &Self) -> bool {
self.asset_id == other.asset_id
&& self.asset_type == other.asset_type
&& self.item_id == other.item_id
&& self.wearable_type == other.wearable_type
}
}
impl Eq for AppearanceManagerWearableData {}
pub(crate) const fn wearable_type_to_asset_type(wearable_type: WearableType) -> AssetType {
match wearable_type {
WearableType::Shape | WearableType::Skin | WearableType::Hair | WearableType::Eyes => {
AssetType::Bodypart
}
WearableType::Invalid => AssetType::Unknown,
_ => AssetType::Clothing,
}
}
fn apply_wearables(
current: &mut Vec<WearableRecord>,
items: Vec<InventoryItem>,
replace: bool,
) -> Result<(), Error> {
let mut seen = HashSet::new();
for item in items {
let wearable = WearableRecord::from_item(&item).ok_or(Error::Argument)?;
if !seen.insert(wearable.item_id)
|| current
.iter()
.any(|existing| existing.item_id == wearable.item_id)
{
continue;
}
if replace || wearable.asset_type == AssetType::Bodypart {
current.retain(|existing| existing.wearable_type != wearable.wearable_type);
} else if current
.iter()
.filter(|existing| existing.wearable_type == wearable.wearable_type)
.count()
>= MAX_WEARABLE_LAYERS
{
return Err(Error::InvalidOperation);
}
current.push(wearable);
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn attachment_packet_bytes(
agent_id: UUID,
session_id: UUID,
item_id: UUID,
owner_id: UUID,
name: &str,
description: &str,
permissions: Permissions,
item_flags: u32,
point: AttachmentPoint,
replace: bool,
) -> Result<Vec<u8>, Error> {
let mut packet = RezSingleAttachmentFromInvPacket::new_with_constructor()?;
packet.agent_data.agent_id = agent_id;
packet.agent_data.session_id = session_id;
packet.object_data.item_id = item_id;
packet.object_data.owner_id = owner_id;
packet.object_data.name = name.as_bytes().to_vec();
packet.object_data.description = description.as_bytes().to_vec();
packet.object_data.everyone_mask = permissions.everyone_mask.0;
packet.object_data.group_mask = permissions.group_mask.0;
packet.object_data.next_owner_mask = permissions.next_owner_mask.0;
packet.object_data.item_flags = item_flags;
packet.object_data.attachment_pt = (point as u8) | if replace { 0 } else { ATTACHMENT_ADD };
packet.to_bytes_with_method()
}
fn send_generated(
network: &crate::NetworkManager,
packet_type: PacketType,
bytes: Vec<u8>,
) -> Result<(), Error> {
let simulator = network
.native_current_sim()
.ok_or(Error::InvalidOperation)?;
let length = i32::try_from(bytes.len()).map_err(|_| Error::Argument)?;
simulator.native_send_packet_data(bytes, length, packet_type, false)
}
fn inventory_item(value: &dyn InventoryObjectClass) -> Option<InventoryItem> {
if let Some(item) = value.as_any().downcast_ref::<InventoryItem>() {
return Some(item.clone());
}
macro_rules! item {
($kind:ty) => {
if let Some(item) = value.as_any().downcast_ref::<$kind>() {
return Some(item.base.clone());
}
};
}
item!(crate::InventoryAnimation);
item!(crate::InventoryAttachment);
item!(crate::InventoryCallingCard);
item!(crate::InventoryCategory);
item!(crate::InventoryGesture);
item!(crate::InventoryLSL);
item!(crate::InventoryLandmark);
item!(crate::InventoryMaterial);
item!(crate::InventoryNotecard);
item!(crate::InventoryObject);
item!(crate::InventorySettings);
item!(crate::InventorySnapshot);
item!(crate::InventorySound);
item!(crate::InventoryTexture);
item!(crate::InventoryWearable);
None
}
#[cfg(test)]
mod tests {
use super::*;
fn wearable(id: u64, wearable_type: WearableType, asset_type: AssetType) -> InventoryItem {
let mut wearable = InventoryWearable::new(UUID::new_with_u_int64(id).unwrap()).unwrap();
wearable.base.set_asset_type(asset_type);
wearable.base.set_inventory_type(InventoryType::WEARABLE);
wearable
.base
.set_asset_uuid(UUID::new_with_u_int64(id + 1_000).unwrap());
wearable.set_wearable_type(wearable_type);
wearable.base
}
#[test]
fn wearable_layers_are_bounded_and_bodyparts_replace_by_slot() {
let mut clothing = Vec::new();
apply_wearables(
&mut clothing,
(1..=60)
.map(|id| wearable(id, WearableType::Shirt, AssetType::Clothing))
.collect(),
false,
)
.unwrap();
assert_eq!(clothing.len(), MAX_WEARABLE_LAYERS);
assert_eq!(
apply_wearables(
&mut clothing,
vec![wearable(61, WearableType::Shirt, AssetType::Clothing)],
false,
),
Err(Error::InvalidOperation)
);
let mut bodyparts = Vec::new();
apply_wearables(
&mut bodyparts,
vec![wearable(100, WearableType::Shape, AssetType::Bodypart)],
false,
)
.unwrap();
apply_wearables(
&mut bodyparts,
vec![wearable(101, WearableType::Shape, AssetType::Bodypart)],
false,
)
.unwrap();
assert_eq!(bodyparts.len(), 1);
assert_eq!(bodyparts[0].item_id, UUID::new_with_u_int64(101).unwrap());
}
#[test]
fn attachment_packet_and_replacement_state_preserve_inventory_fields() {
let client = GridClient::new().unwrap();
let item_id = UUID::new_with_u_int64(500).unwrap();
let owner_id = UUID::new_with_u_int64(501).unwrap();
let permissions = Permissions::new(1, 2, 3, 4, 5).unwrap();
let mut item =
InventoryItem::new_with_inventory_type_uuid(InventoryType::OBJECT, item_id).unwrap();
item.base.set_owner_id(owner_id);
item.base.set_name("recording hat".into());
item.set_description("packet fields".into());
item.set_permissions(permissions);
item.set_flags(0x1234_5678);
item.set_asset_type(AssetType::Object);
let bytes = attachment_packet_bytes(
UUID::zero(),
UUID::zero(),
item_id,
owner_id,
"recording hat",
"packet fields",
permissions,
0x1234_5678,
AttachmentPoint::Skull,
false,
)
.unwrap();
let mut position = 0;
let packet =
RezSingleAttachmentFromInvPacket::new_with_bytes_int32(bytes, &mut position).unwrap();
assert_eq!(packet.object_data.item_id, item_id);
assert_eq!(packet.object_data.owner_id, owner_id);
assert_eq!(packet.object_data.name, b"recording hat");
assert_eq!(packet.object_data.description, b"packet fields");
assert_eq!(packet.object_data.everyone_mask, 2);
assert_eq!(packet.object_data.group_mask, 3);
assert_eq!(packet.object_data.next_owner_mask, 4);
assert_eq!(packet.object_data.item_flags, 0x1234_5678);
assert_eq!(
packet.object_data.attachment_pt,
(AttachmentPoint::Skull as u8) | ATTACHMENT_ADD
);
let manager = client.appearance();
manager.record_attachment(item, AttachmentPoint::Skull, false);
let replacement_id = UUID::new_with_u_int64(502).unwrap();
let mut replacement =
InventoryItem::new_with_inventory_type_uuid(InventoryType::OBJECT, replacement_id)
.unwrap();
replacement.set_permissions(permissions);
manager.record_attachment(replacement, AttachmentPoint::Skull, true);
let state = manager.native_get_attachments_by_id().unwrap();
assert_eq!(state.len(), 1);
assert_eq!(state.get(&replacement_id), Some(&AttachmentPoint::Skull));
}
}

View File

@@ -521,6 +521,7 @@ impl AssetTexture {
}
}
#[derive(Clone, Debug)]
pub struct AssetWearable {
raw: RawAsset,
pub creator: UUID,

View File

@@ -118,6 +118,7 @@ struct ClientRuntime {
caps_rate_limiter: Mutex<crate::caps_http::CapsRateLimiter>,
network_manager: Mutex<std::sync::Weak<crate::network_manager::NetworkManagerInner>>,
agent_manager: Mutex<std::sync::Weak<crate::agent_manager::AgentManagerInner>>,
appearance_manager: Mutex<Option<Arc<crate::appearance_manager::AppearanceManagerInner>>>,
inventory_manager: Mutex<Option<Arc<crate::inventory_manager::InventoryManagerInner>>>,
inventory_ais_client: Mutex<Option<crate::inventory_ais::InventoryAISClient>>,
asset_manager: Mutex<Option<Arc<crate::asset_manager::AssetManagerInner>>>,
@@ -149,6 +150,7 @@ impl ClientRuntime {
caps_rate_limiter: Mutex::new(caps_rate_limiter),
network_manager: Mutex::new(std::sync::Weak::new()),
agent_manager: Mutex::new(std::sync::Weak::new()),
appearance_manager: Mutex::new(None),
inventory_manager: Mutex::new(None),
inventory_ais_client: Mutex::new(None),
asset_manager: Mutex::new(None),
@@ -449,6 +451,32 @@ impl GridClient {
Ok(manager)
}
pub(crate) fn native_appearance(&self) -> Result<crate::AppearanceManager, crate::Error> {
let mut cached = self
.runtime
.appearance_manager
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if let Some(inner) = cached.as_ref() {
return Ok(
crate::appearance_manager::AppearanceManager::native_from_inner(Arc::clone(inner)),
);
}
let manager =
crate::appearance_manager::AppearanceManager::native_new(Some(Arc::new(self.clone())))?;
*cached = Some(manager.native_inner());
Ok(manager)
}
#[allow(clippy::needless_pass_by_value)]
pub(crate) fn native_set_appearance(&mut self, value: crate::AppearanceManager) {
*self
.runtime
.appearance_manager
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = Some(value.native_inner());
}
pub(crate) fn native_assets(&self) -> Result<crate::AssetManager, crate::Error> {
let mut cached = self
.runtime

File diff suppressed because it is too large Load Diff

View File

@@ -2861,12 +2861,9 @@ impl AppearanceFlags {
}
/// C# type: `T:LibreMetaverse.AppearanceManager`.
pub struct AppearanceManager {
/// C# member: `F:LibreMetaverse.AppearanceManager.MyTextures`.
pub my_textures: libremetaverse::PrimitiveTextureEntry,
/// C# member: `F:LibreMetaverse.AppearanceManager.MyVisualParameters`.
pub my_visual_parameters: Vec<u8>,
}
pub use crate::appearance_manager::AppearanceManager;
/// C# member: `F:LibreMetaverse.AppearanceManager.MyTextures`.
/// C# member: `F:LibreMetaverse.AppearanceManager.MyVisualParameters`.
impl AppearanceManager {
/// C# member: `E:LibreMetaverse.AppearanceManager.AgentWearablesReply`.
pub fn subscribe_agent_wearables_reply(
@@ -2875,16 +2872,14 @@ impl AppearanceManager {
libremetaverse::AgentWearablesReplyEventArgs,
>,
) -> libremetaverse_types::compat::Subscription {
libremetaverse_types::unimplemented_api!(
"E:LibreMetaverse.AppearanceManager.AgentWearablesReply"
)
self.native_subscribe_agent_wearables_reply(handler)
}
/// C# member: `E:LibreMetaverse.AppearanceManager.AppearanceSet`.
pub fn subscribe_appearance_set(
&self,
handler: libremetaverse_types::compat::EventHandler<libremetaverse::AppearanceSetEventArgs>,
) -> libremetaverse_types::compat::Subscription {
libremetaverse_types::unimplemented_api!("E:LibreMetaverse.AppearanceManager.AppearanceSet")
self.native_subscribe_appearance_set(handler)
}
/// C# member: `E:LibreMetaverse.AppearanceManager.CachedBakesReply`.
pub fn subscribe_cached_bakes_reply(
@@ -3025,9 +3020,7 @@ impl AppearanceManager {
pub fn new(
client: Option<std::sync::Arc<libremetaverse::GridClient>>,
) -> Result<Self, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.#ctor(LibreMetaverse.GridClient)",
)
crate::appearance_manager::AppearanceManager::native_new(client)
}
/// C# member: `M:LibreMetaverse.AppearanceManager.AddAttachments(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Boolean,System.Boolean)`.
pub fn add_attachments(
@@ -3036,9 +3029,7 @@ impl AppearanceManager {
remove_existing_first: bool,
replace: Option<bool>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.AddAttachments(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Boolean,System.Boolean)",
)
self.native_add_attachments(attachments, remove_existing_first, replace.unwrap_or(false))
}
/// C# member: `M:LibreMetaverse.AppearanceManager.AddToOutfit(LibreMetaverse.InventoryItem,System.Boolean)`.
pub fn add_to_outfit_with_inventory_item_boolean(
@@ -3046,9 +3037,7 @@ impl AppearanceManager {
wearable_item: libremetaverse::InventoryItem,
replace: Option<bool>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.AddToOutfit(LibreMetaverse.InventoryItem,System.Boolean)",
)
self.native_add_to_outfit(vec![wearable_item], replace.unwrap_or(false))
}
/// C# member: `M:LibreMetaverse.AppearanceManager.AddToOutfit(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Boolean)`.
pub fn add_to_outfit_with_list_boolean(
@@ -3056,9 +3045,7 @@ impl AppearanceManager {
wearable_items: Vec<libremetaverse::InventoryItem>,
replace: Option<bool>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.AddToOutfit(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Boolean)",
)
self.native_add_to_outfit(wearable_items, replace.unwrap_or(false))
}
/// C# member: `M:LibreMetaverse.AppearanceManager.ApplyArchetype(LibreMetaverse.GenepoolArchetype)`.
pub async fn apply_archetype(
@@ -3076,9 +3063,7 @@ impl AppearanceManager {
attach_point: libremetaverse_types::AttachmentPoint,
replace: Option<bool>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.Attach(LibreMetaverse.InventoryItem,LibreMetaverse.AttachmentPoint,System.Boolean)",
)
self.native_attach(item, attach_point, replace.unwrap_or(false))
}
/// C# member: `M:LibreMetaverse.AppearanceManager.Attach(LibreMetaverse.UUID,LibreMetaverse.UUID,System.String,System.String,LibreMetaverse.Permissions,System.UInt32,LibreMetaverse.AttachmentPoint,System.Boolean)`.
pub fn attach_with_uuid_uuid_string_string_permissions_u_int32_attachment_point_boolean(
@@ -3092,8 +3077,15 @@ impl AppearanceManager {
attach_point: libremetaverse_types::AttachmentPoint,
replace: Option<bool>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.Attach(LibreMetaverse.UUID,LibreMetaverse.UUID,System.String,System.String,LibreMetaverse.Permissions,System.UInt32,LibreMetaverse.AttachmentPoint,System.Boolean)",
self.native_attach_fields(
item_id,
owner_id,
name,
description,
perms,
item_flags,
attach_point,
replace.unwrap_or(false),
)
}
/// C# member: `M:LibreMetaverse.AppearanceManager.BakeTypeToAgentTextureIndex(LibreMetaverse.BakeType)`.
@@ -3136,28 +3128,24 @@ impl AppearanceManager {
&self,
item: libremetaverse::InventoryItem,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.Detach(LibreMetaverse.InventoryItem)",
)
self.native_detach(item.base.uuid())
}
/// C# member: `M:LibreMetaverse.AppearanceManager.Detach(LibreMetaverse.UUID)`.
pub fn detach_with_uuid(
&self,
item_id: libremetaverse_types::UUID,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.Detach(LibreMetaverse.UUID)",
)
self.native_detach(item_id)
}
/// C# member: `M:LibreMetaverse.AppearanceManager.Dispose`.
pub fn dispose(&self) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.AppearanceManager.Dispose")
self.native_dispose()
}
/// C# member: `M:LibreMetaverse.AppearanceManager.GetAttachments`.
pub fn get_attachments(
&self,
) -> Result<Box<dyn Iterator<Item = libremetaverse::InventoryItem>>, crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.AppearanceManager.GetAttachments")
self.native_get_attachments()
}
/// C# member: `M:LibreMetaverse.AppearanceManager.GetAttachmentsByAttachmentPointAsync(System.Threading.CancellationToken)`.
pub async fn get_attachments_by_attachment_point(
@@ -3170,9 +3158,8 @@ impl AppearanceManager {
>,
crate::Error,
> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.GetAttachmentsByAttachmentPointAsync(System.Threading.CancellationToken)",
)
self.native_get_attachments_by_point(cancellation_token)
.await
}
/// C# member: `M:LibreMetaverse.AppearanceManager.GetAttachmentsByInventoryItem`.
pub fn get_attachments_by_inventory_item(
@@ -3184,9 +3171,7 @@ impl AppearanceManager {
>,
crate::Error,
> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.GetAttachmentsByInventoryItem",
)
self.native_get_attachments_by_item()
}
/// C# member: `M:LibreMetaverse.AppearanceManager.GetAttachmentsByItemId`.
pub fn get_attachments_by_item_id(
@@ -3198,9 +3183,7 @@ impl AppearanceManager {
>,
crate::Error,
> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.GetAttachmentsByItemId",
)
self.native_get_attachments_by_id()
}
/// C# member: `M:LibreMetaverse.AppearanceManager.GetColorFromParams(System.Collections.Generic.List{LibreMetaverse.AppearanceManager.ColorParamInfo})`.
pub fn get_color_from_params(
@@ -3215,9 +3198,8 @@ impl AppearanceManager {
&self,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<Option<libremetaverse::InventoryFolder>, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.GetCurrentOutfitFolderAsync(System.Threading.CancellationToken)",
)
self.native_get_current_outfit_folder(cancellation_token)
.await
}
/// C# member: `M:LibreMetaverse.AppearanceManager.GetCurrentParamValues`.
pub fn get_current_param_values(
@@ -3240,25 +3222,22 @@ impl AppearanceManager {
),
crate::Error,
> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.GetFolderWearablesAsync(LibreMetaverse.UUID,System.Threading.CancellationToken)",
)
self.native_get_folder_wearables(folder, cancellation_token)
.await
}
/// C# member: `M:LibreMetaverse.AppearanceManager.GetWearableAssets(LibreMetaverse.WearableType)`.
pub fn get_wearable_assets(
&self,
type_: libremetaverse_types::WearableType,
) -> Result<Box<dyn Iterator<Item = libremetaverse_types::UUID>>, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.GetWearableAssets(LibreMetaverse.WearableType)",
)
self.native_get_wearable_assets(type_)
}
/// C# member: `M:LibreMetaverse.AppearanceManager.GetWearables`.
pub fn get_wearables(
&self,
) -> Result<Box<dyn Iterator<Item = libremetaverse::AppearanceManagerWearableData>>, crate::Error>
{
libremetaverse_types::not_implemented("M:LibreMetaverse.AppearanceManager.GetWearables")
self.native_get_wearables()
}
/// C# member: `M:LibreMetaverse.AppearanceManager.GetWearablesByType`.
pub fn get_wearables_by_type(
@@ -3270,27 +3249,21 @@ impl AppearanceManager {
>,
crate::Error,
> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.GetWearablesByType",
)
self.native_get_wearables_by_type()
}
/// C# member: `M:LibreMetaverse.AppearanceManager.IsItemWorn(LibreMetaverse.InventoryItem)`.
pub fn is_item_worn_with_inventory_item(
&self,
item: libremetaverse::InventoryItem,
) -> Result<libremetaverse_types::WearableType, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.IsItemWorn(LibreMetaverse.InventoryItem)",
)
self.native_is_item_worn(item.base.uuid())
}
/// C# member: `M:LibreMetaverse.AppearanceManager.IsItemWorn(LibreMetaverse.UUID)`.
pub fn is_item_worn_with_uuid(
&self,
item_id: libremetaverse_types::UUID,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.IsItemWorn(LibreMetaverse.UUID)",
)
Ok(self.native_is_item_worn(item_id)? != libremetaverse_types::WearableType::Invalid)
}
/// C# member: `M:LibreMetaverse.AppearanceManager.MakeAppearancePacket`.
pub fn make_appearance_packet(
@@ -3322,27 +3295,21 @@ impl AppearanceManager {
&self,
wearable_item: libremetaverse::InventoryItem,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.RemoveFromOutfit(LibreMetaverse.InventoryItem)",
)
self.native_remove_from_outfit(vec![wearable_item])
}
/// C# member: `M:LibreMetaverse.AppearanceManager.RemoveFromOutfit(System.Collections.Generic.List{LibreMetaverse.InventoryItem})`.
pub fn remove_from_outfit_with_list(
&self,
wearable_items: Vec<libremetaverse::InventoryItem>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.RemoveFromOutfit(System.Collections.Generic.List{LibreMetaverse.InventoryItem})",
)
self.native_remove_from_outfit(wearable_items)
}
/// C# member: `M:LibreMetaverse.AppearanceManager.ReplaceOutfitAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem})`.
pub async fn replace_outfit_with_list(
&self,
wearable_items: Vec<libremetaverse::InventoryItem>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.ReplaceOutfitAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem})",
)
self.native_replace_outfit(wearable_items, false).await
}
/// C# member: `M:LibreMetaverse.AppearanceManager.ReplaceOutfitAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Boolean)`.
pub async fn replace_outfit_with_list_boolean(
@@ -3350,18 +3317,14 @@ impl AppearanceManager {
wearable_items: Vec<libremetaverse::InventoryItem>,
safe: bool,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.ReplaceOutfitAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Boolean)",
)
self.native_replace_outfit(wearable_items, safe).await
}
/// C# member: `M:LibreMetaverse.AppearanceManager.RequestAgentWornAsync(System.Threading.CancellationToken)`.
pub async fn request_agent_worn(
&self,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<Vec<libremetaverse::InventoryBase>, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.RequestAgentWornAsync(System.Threading.CancellationToken)",
)
self.native_request_agent_worn(cancellation_token).await
}
/// C# member: `M:LibreMetaverse.AppearanceManager.RequestCachedBakes`.
pub fn request_cached_bakes(&self) -> Result<(), crate::Error> {
@@ -3383,9 +3346,7 @@ impl AppearanceManager {
&self,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.SendOutfitToCurrentSimulatorAsync(System.Threading.CancellationToken)",
)
self.native_send_outfit(cancellation_token).await
}
/// C# member: `M:LibreMetaverse.AppearanceManager.ServerBakingRegion`.
pub fn server_baking_region(&self) -> Result<bool, crate::Error> {
@@ -3398,9 +3359,7 @@ impl AppearanceManager {
&mut self,
cof_version: i32,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.UpdateLastReceivedCOFVersion(System.Int32)",
)
self.native_update_cof_version(cof_version)
}
/// C# member: `M:LibreMetaverse.AppearanceManager.WearOutfitAsync(System.Collections.Generic.List{LibreMetaverse.InventoryBase},System.Boolean)`.
pub async fn wear_outfit(
@@ -3408,45 +3367,37 @@ impl AppearanceManager {
wearables: Vec<libremetaverse::InventoryBase>,
replace_items: bool,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.WearOutfitAsync(System.Collections.Generic.List{LibreMetaverse.InventoryBase},System.Boolean)",
)
self.native_wear_outfit(wearables, replace_items).await
}
/// C# member: `M:LibreMetaverse.AppearanceManager.WearableTypeToAssetType(LibreMetaverse.WearableType)`.
pub fn wearable_type_to_asset_type(
type_: libremetaverse_types::WearableType,
) -> Result<libremetaverse_types::AssetType, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.WearableTypeToAssetType(LibreMetaverse.WearableType)",
)
Ok(crate::appearance_manager::wearable_type_to_asset_type(
type_,
))
}
/// C# member: `M:LibreMetaverse.AppearanceManager.isItemAttached(LibreMetaverse.InventoryItem)`.
pub fn is_item_attached_with_inventory_item(
&self,
item: libremetaverse::InventoryItem,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.isItemAttached(LibreMetaverse.InventoryItem)",
)
Ok(self.native_is_item_attached(item.base.uuid()))
}
/// C# member: `M:LibreMetaverse.AppearanceManager.isItemAttached(LibreMetaverse.UUID)`.
pub fn is_item_attached_with_uuid(
&self,
key: libremetaverse_types::UUID,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AppearanceManager.isItemAttached(LibreMetaverse.UUID)",
)
Ok(self.native_is_item_attached(key))
}
/// C# member: `P:LibreMetaverse.AppearanceManager.LastUpdateReceivedCOFVersion`.
pub fn last_update_received_cof_version(&self) -> i32 {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.AppearanceManager.LastUpdateReceivedCOFVersion"
)
self.native_last_cof_version()
}
/// C# member: `P:LibreMetaverse.AppearanceManager.ManagerBusy`.
pub fn manager_busy(&self) -> bool {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AppearanceManager.ManagerBusy")
self.native_manager_busy()
}
/// C# member: `P:LibreMetaverse.AppearanceManager.TextureProvider`.
pub fn texture_provider(&self) -> std::sync::Arc<dyn libremetaverse::IBakingTextureProvider> {
@@ -3507,6 +3458,7 @@ impl AppearanceManagerTextureData {
}
/// C# type: `T:LibreMetaverse.AppearanceManager.WearableData`.
#[derive(Clone, Debug)]
pub struct AppearanceManagerWearableData {
/// C# member: `F:LibreMetaverse.AppearanceManager.WearableData.Asset`.
pub asset: Option<libremetaverse::assets::AssetWearable>,
@@ -8786,11 +8738,14 @@ impl GridClient {
}
/// C# member: `P:LibreMetaverse.GridClient.Appearance`.
pub fn appearance(&self) -> libremetaverse::AppearanceManager {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Appearance")
/* native client-core implementation */
self.native_appearance()
.unwrap_or_else(|_| panic!("failed to construct AppearanceManager"))
}
/// Setter for C# member: `P:LibreMetaverse.GridClient.Appearance`.
pub fn set_appearance(&mut self, value: libremetaverse::AppearanceManager) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GridClient.Appearance")
/* native client-core implementation */
self.native_set_appearance(value)
}
/// C# member: `P:LibreMetaverse.GridClient.Assets`.
pub fn assets(&self) -> libremetaverse::AssetManager {
@@ -14722,9 +14677,13 @@ impl NameValue {
sendto_type: libremetaverse::NameValueSendtoType,
value: libremetaverse_types::compat::Object,
) -> Result<Self, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.NameValue.#ctor(System.String,LibreMetaverse.NameValue.ValueType,LibreMetaverse.NameValue.ClassType,LibreMetaverse.NameValue.SendtoType,System.Object)",
)
Ok(crate::NameValue {
class_: class_type,
name,
sendto: sendto_type,
type_: value_type,
value: Some(value),
})
}
/// C# member: `M:LibreMetaverse.NameValue.Equals(LibreMetaverse.NameValue)`.
pub fn equals_with_name_value(&self, other: libremetaverse::NameValue) -> bool {
@@ -18572,7 +18531,7 @@ pub struct Primitive {
impl Primitive {
/// C# member: `M:LibreMetaverse.Primitive.#ctor`.
pub fn new_with_constructor() -> Result<Self, crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.Primitive.#ctor")
Ok(crate::current_outfit::primitive_defaults())
}
/// C# member: `M:LibreMetaverse.Primitive.#ctor(LibreMetaverse.Primitive)`.
pub fn new_with_primitive(prim: libremetaverse::Primitive) -> Result<Self, crate::Error> {
@@ -23074,13 +23033,11 @@ pub mod animesh {
pub mod appearance {
/// C# type: `T:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy`.
pub struct CompositeCurrentOutfitPolicy;
pub use crate::current_outfit::CompositeCurrentOutfitPolicy;
impl CompositeCurrentOutfitPolicy {
/// C# member: `M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.#ctor`.
pub fn new() -> Result<Self, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.#ctor",
)
crate::current_outfit::CompositeCurrentOutfitPolicy::native_new()
}
/// C# member: `M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.AddPolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)`.
pub fn add_policy(
@@ -23089,27 +23046,21 @@ pub mod appearance {
std::sync::Arc<dyn libremetaverse::appearance::ICurrentOutfitPolicy>,
>,
) -> Result<&Self, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.AddPolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)",
)
self.native_add_policy(policy_to_add)
}
/// C# member: `M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.CanAttach(LibreMetaverse.InventoryItem)`.
pub fn can_attach(
&self,
item: Option<std::sync::Arc<libremetaverse::InventoryItem>>,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.CanAttach(LibreMetaverse.InventoryItem)",
)
self.native_can_attach(item)
}
/// C# member: `M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.CanDetach(LibreMetaverse.InventoryItem)`.
pub fn can_detach(
&self,
item: Option<std::sync::Arc<libremetaverse::InventoryItem>>,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.CanDetach(LibreMetaverse.InventoryItem)",
)
self.native_can_detach(item)
}
/// C# member: `M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.RemovePolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)`.
pub fn remove_policy(
@@ -23118,9 +23069,7 @@ pub mod appearance {
std::sync::Arc<dyn libremetaverse::appearance::ICurrentOutfitPolicy>,
>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.RemovePolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)",
)
self.native_remove_policy(policy_to_remove)
}
/// C# member: `M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Threading.CancellationToken)`.
pub async fn report_item_change(
@@ -23129,23 +23078,19 @@ pub mod appearance {
removed_items: Option<std::sync::Arc<Vec<libremetaverse::InventoryItem>>>,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Threading.CancellationToken)",
)
self.native_report_item_change(added_items, removed_items, cancellation_token)
.await
}
}
impl libremetaverse::appearance::ICurrentOutfitPolicy for CompositeCurrentOutfitPolicy {}
/// C# type: `T:LibreMetaverse.Appearance.CurrentOutfitFolder`.
pub struct CurrentOutfitFolder;
pub use crate::current_outfit::CurrentOutfitFolder;
impl CurrentOutfitFolder {
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.#ctor(LibreMetaverse.GridClient)`.
pub fn new(
client: Option<std::sync::Arc<libremetaverse::GridClient>>,
) -> Result<Self, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.#ctor(LibreMetaverse.GridClient)",
)
crate::current_outfit::CurrentOutfitFolder::native_new(client)
}
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.AddPolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)`.
pub fn add_policy(
@@ -23155,9 +23100,7 @@ pub mod appearance {
std::sync::Arc<dyn libremetaverse::appearance::ICurrentOutfitPolicy>,
crate::Error,
> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.AddPolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)",
)
self.native_add_policy(policy_to_add)
}
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.AddToOutfitAsync(LibreMetaverse.InventoryItem,System.Boolean,System.Threading.CancellationToken)`.
pub async fn add_to_outfit_with_inventory_item_boolean_cancellation_token(
@@ -23166,9 +23109,8 @@ pub mod appearance {
replace: bool,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.AddToOutfitAsync(LibreMetaverse.InventoryItem,System.Boolean,System.Threading.CancellationToken)",
)
self.native_add_to_outfit(vec![item], replace, cancellation_token)
.await
}
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.AddToOutfitAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Boolean,System.Threading.CancellationToken)`.
pub async fn add_to_outfit_with_list_boolean_cancellation_token(
@@ -23177,9 +23119,8 @@ pub mod appearance {
replace: bool,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.AddToOutfitAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Boolean,System.Threading.CancellationToken)",
)
self.native_add_to_outfit(requested_items_to_add, replace, cancellation_token)
.await
}
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.AttachAsync(LibreMetaverse.InventoryItem,LibreMetaverse.AttachmentPoint,System.Boolean,System.Threading.CancellationToken)`.
pub async fn attach(
@@ -23189,9 +23130,8 @@ pub mod appearance {
replace: bool,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.AttachAsync(LibreMetaverse.InventoryItem,LibreMetaverse.AttachmentPoint,System.Boolean,System.Threading.CancellationToken)",
)
self.native_attach(item, point, replace, cancellation_token)
.await
}
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.CanAttachItemAsync(LibreMetaverse.InventoryItem,System.Threading.CancellationToken)`.
pub async fn can_attach_item(
@@ -23199,9 +23139,7 @@ pub mod appearance {
item: libremetaverse::InventoryItem,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.CanAttachItemAsync(LibreMetaverse.InventoryItem,System.Threading.CancellationToken)",
)
self.native_can_attach_item(item, cancellation_token).await
}
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.CanDetachItemAsync(LibreMetaverse.InventoryItem,System.Threading.CancellationToken)`.
pub async fn can_detach_item(
@@ -23209,9 +23147,7 @@ pub mod appearance {
item: libremetaverse::InventoryItem,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.CanDetachItemAsync(LibreMetaverse.InventoryItem,System.Threading.CancellationToken)",
)
self.native_can_detach_item(item, cancellation_token).await
}
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.DetachAsync(LibreMetaverse.InventoryItem,System.Threading.CancellationToken)`.
pub async fn detach(
@@ -23219,15 +23155,11 @@ pub mod appearance {
item: libremetaverse::InventoryItem,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.DetachAsync(LibreMetaverse.InventoryItem,System.Threading.CancellationToken)",
)
self.native_detach(item, cancellation_token).await
}
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.Dispose`.
pub fn dispose(&self) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.Dispose",
)
self.native_dispose()
}
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.FetchParentAsync(LibreMetaverse.InventoryBase,System.Threading.CancellationToken)`.
pub async fn fetch_parent(
@@ -23235,26 +23167,21 @@ pub mod appearance {
item: libremetaverse::InventoryBase,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<Option<libremetaverse::InventoryBase>, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.FetchParentAsync(LibreMetaverse.InventoryBase,System.Threading.CancellationToken)",
)
self.native_fetch_parent(item, cancellation_token).await
}
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.GetAttachmentItemID(LibreMetaverse.Primitive)`.
pub fn get_attachment_item_id(
prim: libremetaverse::Primitive,
) -> Result<libremetaverse_types::UUID, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.GetAttachmentItemID(LibreMetaverse.Primitive)",
)
crate::current_outfit::CurrentOutfitFolder::native_get_attachment_item_id(prim)
}
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.GetCurrentOutfitLinksAsync(System.Threading.CancellationToken)`.
pub async fn get_current_outfit_links(
&self,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<Vec<libremetaverse::InventoryItem>, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.GetCurrentOutfitLinksAsync(System.Threading.CancellationToken)",
)
self.native_get_current_outfit_links(cancellation_token)
.await
}
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.GetWornAtAsync(LibreMetaverse.WearableType,System.Threading.CancellationToken)`.
pub async fn get_worn_at(
@@ -23262,9 +23189,7 @@ pub mod appearance {
type_: libremetaverse_types::WearableType,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<Vec<libremetaverse::InventoryItem>, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.GetWornAtAsync(LibreMetaverse.WearableType,System.Threading.CancellationToken)",
)
self.native_get_worn_at(type_, cancellation_token).await
}
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.IsObjectDescendentOfAsync(LibreMetaverse.InventoryBase,LibreMetaverse.UUID,System.Threading.CancellationToken)`.
pub async fn is_object_descendent_of(
@@ -23273,9 +23198,8 @@ pub mod appearance {
parent_id: libremetaverse_types::UUID,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.IsObjectDescendentOfAsync(LibreMetaverse.InventoryBase,LibreMetaverse.UUID,System.Threading.CancellationToken)",
)
self.native_is_object_descendent_of(item, parent_id, cancellation_token)
.await
}
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.RemoveFromOutfitAsync(LibreMetaverse.InventoryItem,System.Threading.CancellationToken)`.
pub async fn remove_from_outfit_with_inventory_item_cancellation_token(
@@ -23283,9 +23207,8 @@ pub mod appearance {
item: libremetaverse::InventoryItem,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.RemoveFromOutfitAsync(LibreMetaverse.InventoryItem,System.Threading.CancellationToken)",
)
self.native_remove_from_outfit(vec![item], cancellation_token)
.await
}
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.RemoveFromOutfitAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Threading.CancellationToken)`.
pub async fn remove_from_outfit_with_list_cancellation_token(
@@ -23293,18 +23216,15 @@ pub mod appearance {
requested_items_to_remove: Vec<libremetaverse::InventoryItem>,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.RemoveFromOutfitAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Threading.CancellationToken)",
)
self.native_remove_from_outfit(requested_items_to_remove, cancellation_token)
.await
}
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.RemovePolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)`.
pub fn remove_policy(
&self,
policy_to_remove: std::sync::Arc<dyn libremetaverse::appearance::ICurrentOutfitPolicy>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.RemovePolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)",
)
self.native_remove_policy(policy_to_remove)
}
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.ReplaceOutfitAsync(LibreMetaverse.UUID,System.Threading.CancellationToken)`.
pub async fn replace_outfit(
@@ -23312,36 +23232,27 @@ pub mod appearance {
new_outfit_folder_id: libremetaverse_types::UUID,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.ReplaceOutfitAsync(LibreMetaverse.UUID,System.Threading.CancellationToken)",
)
self.native_replace_outfit(new_outfit_folder_id, cancellation_token)
.await
}
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.ResolveInventoryLink(LibreMetaverse.InventoryItem)`.
pub fn resolve_inventory_link(
&self,
item_link: std::sync::Arc<libremetaverse::InventoryItem>,
) -> Result<Option<std::sync::Arc<libremetaverse::InventoryItem>>, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.ResolveInventoryLink(LibreMetaverse.InventoryItem)",
)
self.native_resolve_inventory_link(item_link)
}
/// C# member: `P:LibreMetaverse.Appearance.CurrentOutfitFolder.COF`.
pub fn cof(&self) -> Option<libremetaverse::InventoryFolder> {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.Appearance.CurrentOutfitFolder.COF"
)
self.native_cof()
}
/// Setter for C# member: `P:LibreMetaverse.Appearance.CurrentOutfitFolder.COF`.
pub fn set_cof(&mut self, value: Option<libremetaverse::InventoryFolder>) {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.Appearance.CurrentOutfitFolder.COF"
)
self.native_set_cof(value)
}
/// C# member: `P:LibreMetaverse.Appearance.CurrentOutfitFolder.MaxClothingLayers`.
pub fn max_clothing_layers(&self) -> i32 {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.Appearance.CurrentOutfitFolder.MaxClothingLayers"
)
crate::current_outfit::MAX_CLOTHING_LAYERS
}
}
@@ -23352,18 +23263,14 @@ pub mod appearance {
&self,
item: Option<std::sync::Arc<libremetaverse::InventoryItem>>,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.CanAttach(LibreMetaverse.InventoryItem)",
)
crate::current_outfit::default_policy_decision(item)
}
/// C# member: `M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.CanDetach(LibreMetaverse.InventoryItem)`.
fn can_detach(
&self,
item: Option<std::sync::Arc<libremetaverse::InventoryItem>>,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.CanDetach(LibreMetaverse.InventoryItem)",
)
crate::current_outfit::default_policy_decision(item)
}
/// C# member: `M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Threading.CancellationToken)`.
fn report_item_change(
@@ -23374,11 +23281,11 @@ pub mod appearance {
) -> std::pin::Pin<
Box<dyn std::future::Future<Output = Result<(), crate::Error>> + Send + '_>,
> {
Box::pin(async {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Threading.CancellationToken)",
)
})
crate::current_outfit::default_policy_report(
added_items,
removed_items,
cancellation_token,
)
}
}

View File

@@ -18,6 +18,7 @@ use libremetaverse_types::{
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::fs::{self, File, OpenOptions};
use std::hash::{Hash, Hasher};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::sync::{Arc, RwLock, Weak};
@@ -306,6 +307,24 @@ impl PartialEq for InventoryItem {
impl Eq for InventoryItem {}
impl Hash for InventoryItem {
fn hash<H: Hasher>(&self, state: &mut H) {
self.base.hash(state);
self.asset_uuid.hash(state);
self.asset_type.hash(state);
self.inventory_type.hash(state);
self.description.hash(state);
self.group_id.hash(state);
self.group_owned.hash(state);
self.sale_price.hash(state);
self.sale_type.hash(state);
self.flags.hash(state);
self.creation_date.hash(state);
self.last_owner_id.hash(state);
self.permissions.hash(state);
}
}
impl InventoryItem {
fn with_type(inventory_type: InventoryType, uuid: UUID) -> Self {
Self {

View File

@@ -7,6 +7,7 @@ mod attention_catalog;
mod agent_manager;
mod agent_messages;
mod agent_movement;
mod appearance_manager;
mod asset_cache;
mod asset_manager;
mod asset_material;
@@ -15,6 +16,7 @@ mod bit_pack;
mod caps;
mod caps_http;
mod client_core;
mod current_outfit;
mod download_manager;
mod event_queue;
mod gesture;
@@ -122,41 +124,6 @@ impl animesh::AnimeshPlayer {
}
}
#[allow(dead_code)]
trait CofLinkItem {
fn inventory_item(&self) -> &InventoryItem;
fn wearable_type(&self) -> Option<libremetaverse_types::WearableType>;
}
impl CofLinkItem for InventoryWearable {
fn inventory_item(&self) -> &InventoryItem {
&self.base
}
fn wearable_type(&self) -> Option<libremetaverse_types::WearableType> {
Some(self.wearable_type())
}
}
impl CofLinkItem for InventoryObject {
fn inventory_item(&self) -> &InventoryItem {
&self.base
}
fn wearable_type(&self) -> Option<libremetaverse_types::WearableType> {
None
}
}
#[allow(clippy::unused_self, dead_code)]
impl appearance::CurrentOutfitFolder {
fn get_cof_link_description(&self, _item: &dyn CofLinkItem) -> Result<String, Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.GetCofLinkDescription(LibreMetaverse.InventoryItem)",
)
}
}
#[allow(dead_code)] // Internal C# surface consumed by InventoryManager workflows.
impl InventoryAISClient {
async fn create_inventory_links(