Implement capability event queue processing (#55)
This commit is contained in:
@@ -1,32 +1,44 @@
|
||||
//! Native LLSD field codecs for the translated Linden message fixtures.
|
||||
|
||||
use crate::messages::linden::{
|
||||
AgentGroupDataUpdateMessage, AgentGroupDataUpdateMessageGroupData,
|
||||
AgentGroupDataUpdateMessageNewGroupData, ChatSessionAcceptInvitation, ChatSessionRequestBlock,
|
||||
AgentDropGroupMessage, AgentDropGroupMessageAgentData, AgentGroupDataUpdateMessage,
|
||||
AgentGroupDataUpdateMessageGroupData, AgentGroupDataUpdateMessageNewGroupData,
|
||||
AgentStateUpdateMessage, BulkUpdateInventoryMessage, BulkUpdateInventoryMessageFolderDataInfo,
|
||||
BulkUpdateInventoryMessageItemDataInfo, ChatSessionAcceptInvitation, ChatSessionRequestBlock,
|
||||
ChatSessionRequestMuteUpdate, ChatSessionRequestStartConference, ChatterBoxInvitationMessage,
|
||||
ChatterBoxSessionAgentListUpdatesMessage,
|
||||
ChatterBoxSessionAgentListUpdatesMessageAgentUpdatesBlock, ChatterBoxSessionStartReplyMessage,
|
||||
ChatterboxSessionEventReplyMessage, CopyInventoryFromNotecardMessage, DirLandReplyMessage,
|
||||
DirLandReplyMessageQueryReply, EnableSimulatorMessage,
|
||||
EnableSimulatorMessageSimulatorInfoBlock, EstablishAgentCommunicationMessage,
|
||||
ChatterboxSessionEventReplyMessage, CopyInventoryFromNotecardMessage, CrossedRegionMessage,
|
||||
DirLandReplyMessage, DirLandReplyMessageQueryReply, DisplayNameUpdateMessage,
|
||||
EnableSimulatorMessage, EnableSimulatorMessageSimulatorInfoBlock,
|
||||
EstablishAgentCommunicationMessage, ForceCloseChatterBoxSessionMessage, GetDisplayNamesMessage,
|
||||
GetObjectCostMessage, GetObjectCostRequest, LandResourcesMessage, LandResourcesRequest,
|
||||
LandStatReplyMessage, LandStatReplyMessageReportDataBlock, MapLayerMessageBase,
|
||||
MapLayerReplyVariant, MapLayerReplyVariantLayerData, ParcelObjectOwnersReplyMessage,
|
||||
MapLayerReplyVariant, MapLayerReplyVariantLayerData, NavMeshStatusUpdateMessage,
|
||||
ObjectMediaNavigateMessage, ObjectMediaRequest, ObjectMediaResponse, ObjectMediaUpdate,
|
||||
ObjectPhysicsPropertiesMessage, ParcelObjectOwnersReplyMessage,
|
||||
ParcelObjectOwnersReplyMessagePrimOwner, ParcelPropertiesMessage,
|
||||
ParcelPropertiesUpdateMessage, ParcelVoiceInfoRequestMessage, PlacesReplyMessage,
|
||||
PlacesReplyMessageQueryData, ProvisionVoiceAccountRequestMessage, RemoteParcelRequestReply,
|
||||
RequiredVoiceVersionMessage, ScriptRunningReplyMessage, SendPostcardMessage,
|
||||
TeleportFailedMessage, TeleportFinishMessage, UpdateAgentInformationMessage,
|
||||
UpdateAgentInventoryRequestMessage, UpdateAgentLanguageMessage,
|
||||
UpdateScriptAgentRequestMessage, UpdateScriptTaskUpdateMessage, ViewerStatsMessage,
|
||||
PlacesReplyMessageQueryData, ProvisionVoiceAccountRequestMessage, RegionInfoMessage,
|
||||
RemoteParcelRequestReply, RenderMaterialsMessage, RequiredVoiceVersionMessage,
|
||||
ScriptRunningReplyMessage, SearchStatRequestReply, SearchStatRequestRequest,
|
||||
SendPostcardMessage, SetDisplayNameMessage, SetDisplayNameReplyMessage,
|
||||
SimConsoleResponseMessage, TeleportFailedMessage, TeleportFinishMessage,
|
||||
UpdateAgentInformationMessage, UpdateAgentInventoryRequestMessage, UpdateAgentLanguageMessage,
|
||||
UpdateScriptAgentRequestMessage, UpdateScriptTaskUpdateMessage, UploaderRequestComplete,
|
||||
UploaderRequestUpload, ViewerStatsMessage,
|
||||
};
|
||||
use crate::{
|
||||
Error, GroupPowers, InstantMessageDialog, InstantMessageOnline, LandingType, ParcelCategory,
|
||||
ParcelFlags, ParcelResult, ParcelStatus, SimAccess, TeleportFlags,
|
||||
AgentDisplayName, Error, GroupPowers, InstantMessageDialog, InstantMessageOnline, LandingType,
|
||||
MediaEntry, ParcelCategory, ParcelFlags, ParcelResult, ParcelStatus, PermissionMask,
|
||||
PrimitivePhysicsProperties, SimAccess, TeleportFlags,
|
||||
};
|
||||
use libremetaverse_structured_data::{OSD, OSDMap};
|
||||
use flate2::read::ZlibDecoder;
|
||||
use libremetaverse_structured_data::{OSD, OSDMap, OSDParser};
|
||||
use libremetaverse_types::compat::Uri;
|
||||
use libremetaverse_types::{UUID, Vector3};
|
||||
use libremetaverse_types::{AssetType, FolderType, InventoryType, SaleType, UUID, Vector3};
|
||||
use std::collections::HashMap;
|
||||
use std::io::Read as _;
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
@@ -100,6 +112,18 @@ impl MessageValue for f32 {
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageValue for f64 {
|
||||
fn message_default() -> Self {
|
||||
0.0
|
||||
}
|
||||
fn to_message_osd(&self) -> Result<OSD, Error> {
|
||||
OSD::from_real_with_double(*self)
|
||||
}
|
||||
fn from_message_osd(value: &OSD) -> Result<Self, Error> {
|
||||
value.as_real()
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageValue for String {
|
||||
fn message_default() -> Self {
|
||||
String::new()
|
||||
@@ -181,6 +205,73 @@ impl MessageValue for IpAddr {
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageValue for OSD {
|
||||
fn message_default() -> Self {
|
||||
Self::Undefined
|
||||
}
|
||||
fn to_message_osd(&self) -> Result<OSD, Error> {
|
||||
Ok(self.clone())
|
||||
}
|
||||
fn from_message_osd(value: &OSD) -> Result<Self, Error> {
|
||||
Ok(value.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageValue for OSDMap {
|
||||
fn message_default() -> Self {
|
||||
Self::new_with_constructor().expect("empty OSD map")
|
||||
}
|
||||
fn to_message_osd(&self) -> Result<OSD, Error> {
|
||||
self.copy()
|
||||
}
|
||||
fn from_message_osd(value: &OSD) -> Result<Self, Error> {
|
||||
let OSD::Map(values) = value else {
|
||||
return Err(Error::Argument);
|
||||
};
|
||||
Self::new_with_dictionary(values.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageValue for AgentDisplayName {
|
||||
fn message_default() -> Self {
|
||||
Self::new().expect("AgentDisplayName construction is infallible")
|
||||
}
|
||||
fn to_message_osd(&self) -> Result<OSD, Error> {
|
||||
self.get_osd()
|
||||
}
|
||||
fn from_message_osd(value: &OSD) -> Result<Self, Error> {
|
||||
Self::from_osd(value.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageValue for PrimitivePhysicsProperties {
|
||||
fn message_default() -> Self {
|
||||
Self::new().expect("PrimitivePhysicsProperties construction is infallible")
|
||||
}
|
||||
fn to_message_osd(&self) -> Result<OSD, Error> {
|
||||
self.get_osd()
|
||||
}
|
||||
fn from_message_osd(value: &OSD) -> Result<Self, Error> {
|
||||
Self::from_osd(value.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageValue for MediaEntry {
|
||||
fn message_default() -> Self {
|
||||
Self::new().expect("MediaEntry construction is infallible")
|
||||
}
|
||||
fn to_message_osd(&self) -> Result<OSD, Error> {
|
||||
if self.is_undefined() {
|
||||
Ok(OSD::Undefined)
|
||||
} else {
|
||||
self.get_osd()?.copy()
|
||||
}
|
||||
}
|
||||
fn from_message_osd(value: &OSD) -> Result<Self, Error> {
|
||||
Self::from_osd(value.clone())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! transparent_value {
|
||||
($type:ty, $inner:ty) => {
|
||||
impl MessageValue for $type {
|
||||
@@ -199,6 +290,21 @@ macro_rules! transparent_value {
|
||||
|
||||
transparent_value!(ParcelFlags, u32);
|
||||
transparent_value!(TeleportFlags, u32);
|
||||
transparent_value!(PermissionMask, u32);
|
||||
|
||||
impl MessageValue for InventoryType {
|
||||
fn message_default() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
fn to_message_osd(&self) -> Result<OSD, Error> {
|
||||
OSD::from_integer_with_int32(i32::from(self.0))
|
||||
}
|
||||
fn from_message_osd(value: &OSD) -> Result<Self, Error> {
|
||||
Ok(Self(
|
||||
i8::try_from(value.as_integer()?).map_err(|_| Error::Argument)?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageValue for GroupPowers {
|
||||
fn message_default() -> Self {
|
||||
@@ -289,6 +395,88 @@ enum_value!(
|
||||
InstantMessageDialog::TaskInventoryOffered,
|
||||
]
|
||||
);
|
||||
enum_value!(
|
||||
SaleType,
|
||||
SaleType::Not,
|
||||
[
|
||||
SaleType::Contents,
|
||||
SaleType::Copy,
|
||||
SaleType::Not,
|
||||
SaleType::Original
|
||||
]
|
||||
);
|
||||
enum_value!(
|
||||
AssetType,
|
||||
AssetType::Unknown,
|
||||
[
|
||||
AssetType::Animation,
|
||||
AssetType::Bodypart,
|
||||
AssetType::CallingCard,
|
||||
AssetType::Clothing,
|
||||
AssetType::Folder,
|
||||
AssetType::Gesture,
|
||||
AssetType::ImageJPEG,
|
||||
AssetType::ImageTGA,
|
||||
AssetType::LSLBytecode,
|
||||
AssetType::LSLText,
|
||||
AssetType::Landmark,
|
||||
AssetType::Link,
|
||||
AssetType::LinkFolder,
|
||||
AssetType::Material,
|
||||
AssetType::Mesh,
|
||||
AssetType::Notecard,
|
||||
AssetType::Object,
|
||||
AssetType::Person,
|
||||
AssetType::Script,
|
||||
AssetType::Settings,
|
||||
AssetType::Simstate,
|
||||
AssetType::Sound,
|
||||
AssetType::SoundWAV,
|
||||
AssetType::Texture,
|
||||
AssetType::TextureTGA,
|
||||
AssetType::Unknown,
|
||||
AssetType::Widget
|
||||
]
|
||||
);
|
||||
enum_value!(
|
||||
FolderType,
|
||||
FolderType::None,
|
||||
[
|
||||
FolderType::Animation,
|
||||
FolderType::BasicRoot,
|
||||
FolderType::BodyPart,
|
||||
FolderType::CallingCard,
|
||||
FolderType::Clothing,
|
||||
FolderType::CurrentOutfit,
|
||||
FolderType::EnsembleEnd,
|
||||
FolderType::EnsembleStart,
|
||||
FolderType::Favorites,
|
||||
FolderType::Gesture,
|
||||
FolderType::Inbox,
|
||||
FolderType::LSLText,
|
||||
FolderType::Landmark,
|
||||
FolderType::LostAndFound,
|
||||
FolderType::MarketplaceListings,
|
||||
FolderType::MarketplaceStock,
|
||||
FolderType::MarketplaceVersion,
|
||||
FolderType::Material,
|
||||
FolderType::Mesh,
|
||||
FolderType::MyOutfits,
|
||||
FolderType::None,
|
||||
FolderType::Notecard,
|
||||
FolderType::Object,
|
||||
FolderType::OldRoot,
|
||||
FolderType::Outbox,
|
||||
FolderType::Outfit,
|
||||
FolderType::Root,
|
||||
FolderType::Settings,
|
||||
FolderType::Snapshot,
|
||||
FolderType::Sound,
|
||||
FolderType::Suitcase,
|
||||
FolderType::Texture,
|
||||
FolderType::Trash
|
||||
]
|
||||
);
|
||||
enum_value!(
|
||||
InstantMessageOnline,
|
||||
InstantMessageOnline::Online,
|
||||
@@ -444,6 +632,471 @@ fn integer_as_f32(value: &OSD) -> Result<f32, Error> {
|
||||
Ok(value.as_integer()? as f32)
|
||||
}
|
||||
|
||||
fn serialize_agent_state_update(value: &AgentStateUpdateMessage) -> Result<OSDMap, Error> {
|
||||
let access = map_osd([("max", value.max_access.to_message_osd()?)]);
|
||||
let preferences = map_osd([
|
||||
("access_prefs", access),
|
||||
(
|
||||
"alter_navmesh_objects",
|
||||
value.alter_navmesh_objects.to_message_osd()?,
|
||||
),
|
||||
(
|
||||
"alter_permanent_objects",
|
||||
value.alter_permanent_objects.to_message_osd()?,
|
||||
),
|
||||
("god_level", value.god_level.to_message_osd()?),
|
||||
("language", value.language.to_message_osd()?),
|
||||
(
|
||||
"language_is_public",
|
||||
value.language_is_public.to_message_osd()?,
|
||||
),
|
||||
]);
|
||||
finish_map([
|
||||
(
|
||||
"can_modify_navmesh",
|
||||
value.can_modify_navmesh.to_message_osd()?,
|
||||
),
|
||||
(
|
||||
"has_modified_navmesh",
|
||||
value.has_modified_navmesh.to_message_osd()?,
|
||||
),
|
||||
("preferences", preferences),
|
||||
])
|
||||
}
|
||||
|
||||
fn deserialize_agent_state_update(
|
||||
value: &mut AgentStateUpdateMessage,
|
||||
map: &OSDMap,
|
||||
) -> Result<(), Error> {
|
||||
value.raw_data = map.clone();
|
||||
value.can_modify_navmesh = map
|
||||
.get("can_modify_navmesh")
|
||||
.unwrap_or_default()
|
||||
.as_boolean()?;
|
||||
value.has_modified_navmesh = map
|
||||
.get("has_modified_navmesh")
|
||||
.unwrap_or_default()
|
||||
.as_boolean()?;
|
||||
let Some(OSD::Map(preferences)) = map.get("preferences") else {
|
||||
return Ok(());
|
||||
};
|
||||
let preferences = OSDMap::new_with_dictionary(preferences)?;
|
||||
value.alter_navmesh_objects = preferences
|
||||
.get("alter_navmesh_objects")
|
||||
.unwrap_or_default()
|
||||
.as_boolean()?;
|
||||
value.alter_permanent_objects = preferences
|
||||
.get("alter_permanent_objects")
|
||||
.unwrap_or_default()
|
||||
.as_boolean()?;
|
||||
value.god_level = preferences
|
||||
.get("god_level")
|
||||
.unwrap_or_default()
|
||||
.as_integer()?;
|
||||
value.language = preferences
|
||||
.get("language")
|
||||
.unwrap_or_default()
|
||||
.as_string()?;
|
||||
value.language_is_public = preferences
|
||||
.get("language_is_public")
|
||||
.unwrap_or_default()
|
||||
.as_boolean()?;
|
||||
if let Some(OSD::Map(access)) = preferences.get("access_prefs") {
|
||||
value.max_access = access.get("max").unwrap_or(&OSD::Undefined).as_string()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_crossed_region(value: &CrossedRegionMessage) -> Result<OSDMap, Error> {
|
||||
finish_map([
|
||||
(
|
||||
"Info",
|
||||
one_block_osd([
|
||||
("LookAt", value.look_at.to_message_osd()?),
|
||||
("Position", value.position.to_message_osd()?),
|
||||
]),
|
||||
),
|
||||
(
|
||||
"AgentData",
|
||||
one_block_osd([
|
||||
("AgentID", value.agent_id.to_message_osd()?),
|
||||
("SessionID", value.session_id.to_message_osd()?),
|
||||
]),
|
||||
),
|
||||
(
|
||||
"RegionData",
|
||||
one_block_osd([
|
||||
("RegionHandle", value.region_handle.to_message_osd()?),
|
||||
("SeedCapability", value.seed_capability.to_message_osd()?),
|
||||
("SimIP", value.ip.to_message_osd()?),
|
||||
("SimPort", value.port.to_message_osd()?),
|
||||
("RegionSizeX", value.region_size_x.to_message_osd()?),
|
||||
("RegionSizeY", value.region_size_y.to_message_osd()?),
|
||||
]),
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
fn first_block(map: &OSDMap, key: &str) -> Result<OSDMap, Error> {
|
||||
let encoded = required(map, key)?;
|
||||
let blocks = as_array(&encoded)?;
|
||||
let block = as_map(blocks.first().ok_or(Error::Argument)?)?;
|
||||
OSDMap::new_with_dictionary(block.clone())
|
||||
}
|
||||
|
||||
fn deserialize_crossed_region(value: &mut CrossedRegionMessage, map: &OSDMap) -> Result<(), Error> {
|
||||
let info = first_block(map, "Info")?;
|
||||
value.look_at = decode_top(&info, "LookAt")?;
|
||||
value.position = decode_top(&info, "Position")?;
|
||||
let agent = first_block(map, "AgentData")?;
|
||||
value.agent_id = decode_top(&agent, "AgentID")?;
|
||||
value.session_id = decode_top(&agent, "SessionID")?;
|
||||
let region = first_block(map, "RegionData")?;
|
||||
value.region_handle = decode_top(®ion, "RegionHandle")?;
|
||||
value.seed_capability = decode_top(®ion, "SeedCapability")?;
|
||||
value.ip = decode_top(®ion, "SimIP")?;
|
||||
value.port = decode_top(®ion, "SimPort")?;
|
||||
if region.get("RegionSizeX").is_some() {
|
||||
value.region_size_x = decode_top(®ion, "RegionSizeX")?;
|
||||
}
|
||||
if region.get("RegionSizeY").is_some() {
|
||||
value.region_size_y = decode_top(®ion, "RegionSizeY")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_get_object_cost(value: &GetObjectCostMessage) -> Result<OSDMap, Error> {
|
||||
let values = HashMap::from([
|
||||
(
|
||||
"linked_set_resource_cost".to_owned(),
|
||||
OSD::Real(value.link_cost),
|
||||
),
|
||||
("resource_cost".to_owned(), OSD::Real(value.object_cost)),
|
||||
("physics_cost".to_owned(), OSD::Real(value.physics_cost)),
|
||||
(
|
||||
"linked_set_physics_cost".to_owned(),
|
||||
OSD::Real(value.link_physics_cost),
|
||||
),
|
||||
]);
|
||||
OSDMap::new_with_dictionary(HashMap::from([(
|
||||
value.object_id.to_string(),
|
||||
OSD::Map(values),
|
||||
)]))
|
||||
}
|
||||
|
||||
fn deserialize_get_object_cost(
|
||||
value: &mut GetObjectCostMessage,
|
||||
map: &OSDMap,
|
||||
) -> Result<(), Error> {
|
||||
let mut entries: Vec<_> = map.snapshot().into_iter().collect();
|
||||
entries.sort_unstable_by(|left, right| left.0.cmp(&right.0));
|
||||
let Some((id, OSD::Map(costs))) = entries.into_iter().next() else {
|
||||
return Err(Error::Argument);
|
||||
};
|
||||
value.object_id = UUID::new_with_string(id).unwrap_or_else(|_| UUID::zero());
|
||||
value.link_cost = costs
|
||||
.get("linked_set_resource_cost")
|
||||
.unwrap_or(&OSD::Undefined)
|
||||
.as_real()?;
|
||||
value.object_cost = costs
|
||||
.get("resource_cost")
|
||||
.unwrap_or(&OSD::Undefined)
|
||||
.as_real()?;
|
||||
value.physics_cost = costs
|
||||
.get("physics_cost")
|
||||
.unwrap_or(&OSD::Undefined)
|
||||
.as_real()?;
|
||||
value.link_physics_cost = costs
|
||||
.get("linked_set_physics_cost")
|
||||
.unwrap_or(&OSD::Undefined)
|
||||
.as_real()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_nav_mesh_status(value: &NavMeshStatusUpdateMessage) -> Result<OSDMap, Error> {
|
||||
finish_map([
|
||||
("region_id", value.region_id.to_message_osd()?),
|
||||
("status", value.status.to_message_osd()?),
|
||||
("version", value.version.to_message_osd()?),
|
||||
])
|
||||
}
|
||||
|
||||
fn deserialize_nav_mesh_status(
|
||||
value: &mut NavMeshStatusUpdateMessage,
|
||||
map: &OSDMap,
|
||||
) -> Result<(), Error> {
|
||||
value.raw_data = map.clone();
|
||||
value.region_id = decode_top(map, "region_id")?;
|
||||
value.status = decode_top(map, "status")?;
|
||||
value.version = decode_top(map, "version")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_region_info(value: &RegionInfoMessage) -> Result<OSDMap, Error> {
|
||||
finish_map([
|
||||
("parcel_local_id", value.parcel_local_id.to_message_osd()?),
|
||||
("region_name", value.region_name.to_message_osd()?),
|
||||
(
|
||||
"voice_credentials",
|
||||
map_osd([("channel_uri", value.channel_uri.to_message_osd()?)]),
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
fn deserialize_region_info(value: &mut RegionInfoMessage, map: &OSDMap) -> Result<(), Error> {
|
||||
value.parcel_local_id = decode_top(map, "parcel_local_id")?;
|
||||
value.region_name = decode_top(map, "region_name")?;
|
||||
let OSD::Map(voice) = required(map, "voice_credentials")? else {
|
||||
return Err(Error::Argument);
|
||||
};
|
||||
value.channel_uri = voice
|
||||
.get("channel_uri")
|
||||
.unwrap_or(&OSD::Undefined)
|
||||
.as_string()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_render_materials(_value: &RenderMaterialsMessage) -> Result<OSDMap, Error> {
|
||||
OSDMap::new_with_constructor()
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_wraps)] // GeneratedMessage requires a fallible codec signature.
|
||||
fn deserialize_render_materials(
|
||||
value: &mut RenderMaterialsMessage,
|
||||
map: &OSDMap,
|
||||
) -> Result<(), Error> {
|
||||
let decoded = map
|
||||
.get("Zipped")
|
||||
.ok_or(Error::Argument)
|
||||
.and_then(|value| value.as_binary())
|
||||
.and_then(|bytes| {
|
||||
let mut output = Vec::new();
|
||||
ZlibDecoder::new(bytes.as_slice())
|
||||
.read_to_end(&mut output)
|
||||
.map_err(|_| Error::Argument)?;
|
||||
OSDParser::deserialize_llsd_binary_with_bytes(output)
|
||||
});
|
||||
value.material_data = decoded.unwrap_or_else(|_| OSD::Map(HashMap::new()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_set_display_name(value: &SetDisplayNameMessage) -> Result<OSDMap, Error> {
|
||||
finish_map([(
|
||||
"display_name",
|
||||
OSD::Array(vec![
|
||||
OSD::String(value.old_display_name.clone()),
|
||||
OSD::String(value.new_display_name.clone()),
|
||||
]),
|
||||
)])
|
||||
}
|
||||
|
||||
fn deserialize_set_display_name(
|
||||
value: &mut SetDisplayNameMessage,
|
||||
map: &OSDMap,
|
||||
) -> Result<(), Error> {
|
||||
let OSD::Array(names) = required(map, "display_name")? else {
|
||||
return Err(Error::Argument);
|
||||
};
|
||||
value.old_display_name = names.first().unwrap_or(&OSD::Undefined).as_string()?;
|
||||
value.new_display_name = names.get(1).unwrap_or(&OSD::Undefined).as_string()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_set_display_name_reply(value: &SetDisplayNameReplyMessage) -> Result<OSDMap, Error> {
|
||||
finish_map([
|
||||
("content", value.display_name.to_message_osd()?),
|
||||
("reason", value.reason.to_message_osd()?),
|
||||
("status", value.status.to_message_osd()?),
|
||||
])
|
||||
}
|
||||
|
||||
fn deserialize_set_display_name_reply(
|
||||
value: &mut SetDisplayNameReplyMessage,
|
||||
map: &OSDMap,
|
||||
) -> Result<(), Error> {
|
||||
value.display_name = decode_top(map, "content")?;
|
||||
value.reason = decode_top(map, "reason")?;
|
||||
value.status = decode_top(map, "status")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_display_name_update(value: &DisplayNameUpdateMessage) -> Result<OSDMap, Error> {
|
||||
let OSD::Map(mut agent) = value.display_name.to_message_osd()? else {
|
||||
return Err(Error::Argument);
|
||||
};
|
||||
agent.insert(
|
||||
"old_display_name".into(),
|
||||
OSD::String(value.old_display_name.clone()),
|
||||
);
|
||||
finish_map([("agent", OSD::Map(agent))])
|
||||
}
|
||||
|
||||
fn deserialize_display_name_update(
|
||||
value: &mut DisplayNameUpdateMessage,
|
||||
map: &OSDMap,
|
||||
) -> Result<(), Error> {
|
||||
let agent = required(map, "agent")?;
|
||||
let OSD::Map(agent_map) = &agent else {
|
||||
return Err(Error::Argument);
|
||||
};
|
||||
value.display_name = AgentDisplayName::from_osd(agent.clone())?;
|
||||
value.old_display_name = agent_map
|
||||
.get("old_display_name")
|
||||
.unwrap_or(&OSD::Undefined)
|
||||
.as_string()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_uploader_upload(value: &UploaderRequestUpload) -> Result<OSDMap, Error> {
|
||||
finish_map([
|
||||
("state", OSD::String("upload".into())),
|
||||
("uploader", value.url.to_message_osd()?),
|
||||
])
|
||||
}
|
||||
|
||||
fn deserialize_uploader_upload(
|
||||
value: &mut UploaderRequestUpload,
|
||||
map: &OSDMap,
|
||||
) -> Result<(), Error> {
|
||||
if required(map, "state")?.as_string()? != "upload" {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
value.url = decode_top(map, "uploader")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_uploader_complete(value: &UploaderRequestComplete) -> Result<OSDMap, Error> {
|
||||
finish_map([
|
||||
("state", OSD::String("complete".into())),
|
||||
("new_asset", value.asset_id.to_message_osd()?),
|
||||
])
|
||||
}
|
||||
|
||||
fn deserialize_uploader_complete(
|
||||
value: &mut UploaderRequestComplete,
|
||||
map: &OSDMap,
|
||||
) -> Result<(), Error> {
|
||||
if required(map, "state")?.as_string()? != "complete" {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
value.asset_id = decode_top(map, "new_asset")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_bulk_folder(
|
||||
value: &BulkUpdateInventoryMessageFolderDataInfo,
|
||||
) -> Result<OSDMap, Error> {
|
||||
finish_map([
|
||||
("FolderID", value.folder_id.to_message_osd()?),
|
||||
("ParentID", value.parent_id.to_message_osd()?),
|
||||
("Name", value.name.to_message_osd()?),
|
||||
("Type", value.type_.to_message_osd()?),
|
||||
])
|
||||
}
|
||||
|
||||
fn deserialize_bulk_folder(
|
||||
value: &mut BulkUpdateInventoryMessageFolderDataInfo,
|
||||
map: &OSDMap,
|
||||
) -> Result<(), Error> {
|
||||
value.folder_id = decode_top(map, "FolderID")?;
|
||||
value.parent_id = decode_top(map, "ParentID")?;
|
||||
value.name = decode_top(map, "Name")?;
|
||||
value.type_ = decode_top(map, "Type")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_bulk_item(value: &BulkUpdateInventoryMessageItemDataInfo) -> Result<OSDMap, Error> {
|
||||
finish_map([
|
||||
("ItemID", value.item_id.to_message_osd()?),
|
||||
("CallbackID", value.callback_id.to_message_osd()?),
|
||||
("FolderID", value.folder_id.to_message_osd()?),
|
||||
("CreatorID", value.creator_id.to_message_osd()?),
|
||||
("OwnerID", value.owner_id.to_message_osd()?),
|
||||
("GroupID", value.group_id.to_message_osd()?),
|
||||
("BaseMask", value.base_mask.to_message_osd()?),
|
||||
("OwnerMask", value.owner_mask.to_message_osd()?),
|
||||
("GroupMask", value.group_mask.to_message_osd()?),
|
||||
("EveryoneMask", value.everyone_mask.to_message_osd()?),
|
||||
("NextOwnerMask", value.next_owner_mask.to_message_osd()?),
|
||||
("GroupOwned", value.group_owned.to_message_osd()?),
|
||||
("AssetID", value.asset_id.to_message_osd()?),
|
||||
("Type", value.type_.to_message_osd()?),
|
||||
("InvType", value.inv_type.to_message_osd()?),
|
||||
("Flags", value.flags.to_message_osd()?),
|
||||
("SaleType", value.sale_type.to_message_osd()?),
|
||||
("SalePrice", value.sale_price.to_message_osd()?),
|
||||
("Name", value.name.to_message_osd()?),
|
||||
("Description", value.description.to_message_osd()?),
|
||||
(
|
||||
"CreationDate",
|
||||
unix_seconds(value.creation_date).to_message_osd()?,
|
||||
),
|
||||
("CRC", value.crc.to_message_osd()?),
|
||||
])
|
||||
}
|
||||
|
||||
fn deserialize_bulk_item(
|
||||
value: &mut BulkUpdateInventoryMessageItemDataInfo,
|
||||
map: &OSDMap,
|
||||
) -> Result<(), Error> {
|
||||
value.item_id = decode_top(map, "ItemID")?;
|
||||
value.callback_id = decode_top(map, "CallbackID")?;
|
||||
value.folder_id = decode_top(map, "FolderID")?;
|
||||
value.creator_id = decode_top(map, "CreatorID")?;
|
||||
value.owner_id = decode_top(map, "OwnerID")?;
|
||||
value.group_id = decode_top(map, "GroupID")?;
|
||||
value.base_mask = decode_top(map, "BaseMask")?;
|
||||
value.owner_mask = decode_top(map, "OwnerMask")?;
|
||||
value.group_mask = decode_top(map, "GroupMask")?;
|
||||
value.everyone_mask = decode_top(map, "EveryoneMask")?;
|
||||
value.next_owner_mask = decode_top(map, "NextOwnerMask")?;
|
||||
value.group_owned = decode_top(map, "GroupOwned")?;
|
||||
value.asset_id = decode_top(map, "AssetID")?;
|
||||
value.type_ = decode_top(map, "Type")?;
|
||||
value.inv_type = decode_top(map, "InvType")?;
|
||||
value.flags = decode_top(map, "Flags")?;
|
||||
value.sale_type = decode_top(map, "SaleType")?;
|
||||
// Preserve the reference adapter's field selection for wire compatibility.
|
||||
value.sale_price = decode_top(map, "SaleType")?;
|
||||
value.name = decode_top(map, "Name")?;
|
||||
value.description = decode_top(map, "Description")?;
|
||||
value.creation_date = time_from_unix(decode_top(map, "CreationDate")?);
|
||||
value.crc = decode_top(map, "CRC")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_bulk_inventory(value: &BulkUpdateInventoryMessage) -> Result<OSDMap, Error> {
|
||||
finish_map([
|
||||
(
|
||||
"AgentData",
|
||||
one_block_osd([
|
||||
("AgentID", value.agent_id.to_message_osd()?),
|
||||
("TransactionID", value.transaction_id.to_message_osd()?),
|
||||
]),
|
||||
),
|
||||
("FolderData", encode_struct_array(&value.folder_data)?),
|
||||
("ItemData", encode_struct_array(&value.item_data)?),
|
||||
])
|
||||
}
|
||||
|
||||
fn deserialize_bulk_inventory(
|
||||
value: &mut BulkUpdateInventoryMessage,
|
||||
map: &OSDMap,
|
||||
) -> Result<(), Error> {
|
||||
let agent = first_block(map, "AgentData")?;
|
||||
value.agent_id = decode_top(&agent, "AgentID")?;
|
||||
value.transaction_id = decode_top(&agent, "TransactionID")?;
|
||||
value.folder_data = match map.get("FolderData") {
|
||||
Some(encoded) => decode_array(&encoded)?,
|
||||
None => Vec::new(),
|
||||
};
|
||||
value.item_data = match map.get("ItemData") {
|
||||
Some(encoded) => decode_array(&encoded)?,
|
||||
None => Vec::new(),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
macro_rules! map_message {
|
||||
($type:ty { scalar: [$($scalar:ident => $scalar_key:literal),* $(,)?], array: [$($array:ident => $array_key:literal),* $(,)?], bytes: [$($bytes:ident => $bytes_key:literal),* $(,)?] } $(init $init:expr)?) => {
|
||||
impl GeneratedMessage for $type {
|
||||
@@ -549,6 +1202,36 @@ map_message!(AgentGroupDataUpdateMessageNewGroupData {
|
||||
array: [],
|
||||
bytes: []
|
||||
});
|
||||
map_message!(AgentDropGroupMessageAgentData {
|
||||
scalar: [agent_id => "AgentID", group_id => "GroupID"],
|
||||
array: [],
|
||||
bytes: []
|
||||
});
|
||||
map_message!(AgentDropGroupMessage {
|
||||
scalar: [],
|
||||
array: [agent_data_block => "AgentData"],
|
||||
bytes: []
|
||||
});
|
||||
custom_message!(BulkUpdateInventoryMessageFolderDataInfo {
|
||||
scalar: [folder_id, name, parent_id, type_],
|
||||
array: [],
|
||||
bytes: []
|
||||
} => serialize_bulk_folder, deserialize_bulk_folder);
|
||||
custom_message!(BulkUpdateInventoryMessageItemDataInfo {
|
||||
scalar: [asset_id, base_mask, crc, callback_id, creation_date, creator_id, description, everyone_mask, flags, folder_id, group_id, group_mask, group_owned, inv_type, item_id, name, next_owner_mask, owner_id, owner_mask, sale_price, sale_type, type_],
|
||||
array: [],
|
||||
bytes: []
|
||||
} => serialize_bulk_item, deserialize_bulk_item);
|
||||
custom_message!(BulkUpdateInventoryMessage {
|
||||
scalar: [agent_id, transaction_id],
|
||||
array: [folder_data, item_data],
|
||||
bytes: []
|
||||
} => serialize_bulk_inventory, deserialize_bulk_inventory);
|
||||
custom_message!(AgentStateUpdateMessage {
|
||||
scalar: [alter_navmesh_objects, alter_permanent_objects, can_modify_navmesh, god_level, has_modified_navmesh, language, language_is_public, max_access, raw_data],
|
||||
array: [],
|
||||
bytes: []
|
||||
} => serialize_agent_state_update, deserialize_agent_state_update);
|
||||
custom_message!(AgentGroupDataUpdateMessage {
|
||||
scalar: [agent_id, avatar_id],
|
||||
array: [group_data_block, new_group_data_block],
|
||||
@@ -583,6 +1266,14 @@ custom_message!(ChatterBoxInvitationMessage {
|
||||
array: [],
|
||||
bytes: [binary_bucket]
|
||||
} => serialize_chatter_invitation, deserialize_chatter_invitation);
|
||||
custom_message!(CrossedRegionMessage {
|
||||
scalar: [agent_id, ip, look_at, port, position, region_handle, region_size_x, region_size_y, seed_capability, session_id],
|
||||
array: [],
|
||||
bytes: []
|
||||
} => serialize_crossed_region, deserialize_crossed_region, init |value: &mut CrossedRegionMessage| {
|
||||
value.region_size_x = crate::network_manager::Simulator::DEFAULT_REGION_SIZE_X;
|
||||
value.region_size_y = crate::network_manager::Simulator::DEFAULT_REGION_SIZE_Y;
|
||||
});
|
||||
map_message!(ChatterBoxSessionAgentListUpdatesMessageAgentUpdatesBlock {
|
||||
scalar: [
|
||||
agent_id => "agent_id",
|
||||
@@ -859,6 +1550,96 @@ map_message!(RemoteParcelRequestReply {
|
||||
array: [],
|
||||
bytes: []
|
||||
});
|
||||
map_message!(SearchStatRequestRequest {
|
||||
scalar: [classified_id => "classified_id"],
|
||||
array: [],
|
||||
bytes: []
|
||||
});
|
||||
map_message!(SearchStatRequestReply {
|
||||
scalar: [
|
||||
map_clicks => "map_clicks",
|
||||
profile_clicks => "profile_clicks",
|
||||
search_map_clicks => "search_map_clicks",
|
||||
search_profile_clicks => "search_profile_clicks",
|
||||
search_teleport_clicks => "search_teleport_clicks",
|
||||
teleport_clicks => "teleport_clicks"
|
||||
],
|
||||
array: [],
|
||||
bytes: []
|
||||
});
|
||||
map_message!(ForceCloseChatterBoxSessionMessage {
|
||||
scalar: [reason => "reason", session_id => "session_id"],
|
||||
array: [],
|
||||
bytes: []
|
||||
});
|
||||
map_message!(GetDisplayNamesMessage {
|
||||
scalar: [],
|
||||
array: [agents => "agents", bad_i_ds => "bad_ids"],
|
||||
bytes: []
|
||||
});
|
||||
custom_message!(GetObjectCostMessage {
|
||||
scalar: [link_cost, link_physics_cost, object_cost, object_id, physics_cost],
|
||||
array: [],
|
||||
bytes: []
|
||||
} => serialize_get_object_cost, deserialize_get_object_cost);
|
||||
map_message!(GetObjectCostRequest {
|
||||
scalar: [],
|
||||
array: [object_i_ds => "object_ids"],
|
||||
bytes: []
|
||||
});
|
||||
map_message!(LandResourcesMessage {
|
||||
scalar: [
|
||||
script_resource_details => "ScriptResourceDetails",
|
||||
script_resource_summary => "ScriptResourceSummary"
|
||||
],
|
||||
array: [],
|
||||
bytes: []
|
||||
});
|
||||
map_message!(LandResourcesRequest {
|
||||
scalar: [parcel_id => "parcel_id"],
|
||||
array: [],
|
||||
bytes: []
|
||||
});
|
||||
custom_message!(NavMeshStatusUpdateMessage {
|
||||
scalar: [raw_data, region_id, status, version],
|
||||
array: [],
|
||||
bytes: []
|
||||
} => serialize_nav_mesh_status, deserialize_nav_mesh_status);
|
||||
map_message!(ObjectMediaNavigateMessage {
|
||||
scalar: [face => "texture_index", prim_id => "object_id", url => "current_url"],
|
||||
array: [],
|
||||
bytes: []
|
||||
});
|
||||
map_message!(ObjectMediaRequest {
|
||||
scalar: [prim_id => "object_id", verb => "verb"],
|
||||
array: [],
|
||||
bytes: []
|
||||
} init |value: &mut ObjectMediaRequest| value.verb = "GET".into());
|
||||
map_message!(ObjectMediaResponse {
|
||||
scalar: [prim_id => "object_id", version => "object_media_version"],
|
||||
array: [face_media => "object_media_data"],
|
||||
bytes: []
|
||||
});
|
||||
map_message!(ObjectMediaUpdate {
|
||||
scalar: [prim_id => "object_id", verb => "verb"],
|
||||
array: [face_media => "object_media_data"],
|
||||
bytes: []
|
||||
} init |value: &mut ObjectMediaUpdate| value.verb = "UPDATE".into());
|
||||
map_message!(ObjectPhysicsPropertiesMessage {
|
||||
scalar: [],
|
||||
array: [object_physics_properties => "ObjectData"],
|
||||
bytes: []
|
||||
});
|
||||
custom_message!(RegionInfoMessage {
|
||||
scalar: [channel_uri, parcel_local_id, region_name],
|
||||
array: [],
|
||||
bytes: []
|
||||
} => serialize_region_info, deserialize_region_info);
|
||||
custom_message!(RenderMaterialsMessage {
|
||||
scalar: [material_data],
|
||||
array: [],
|
||||
bytes: []
|
||||
} => serialize_render_materials, deserialize_render_materials);
|
||||
map_message!(RequiredVoiceVersionMessage {
|
||||
scalar: [
|
||||
major_version => "major_version",
|
||||
@@ -884,6 +1665,26 @@ map_message!(SendPostcardMessage {
|
||||
array: [],
|
||||
bytes: []
|
||||
});
|
||||
custom_message!(SetDisplayNameMessage {
|
||||
scalar: [new_display_name, old_display_name],
|
||||
array: [],
|
||||
bytes: []
|
||||
} => serialize_set_display_name, deserialize_set_display_name);
|
||||
custom_message!(SetDisplayNameReplyMessage {
|
||||
scalar: [display_name, reason, status],
|
||||
array: [],
|
||||
bytes: []
|
||||
} => serialize_set_display_name_reply, deserialize_set_display_name_reply);
|
||||
custom_message!(DisplayNameUpdateMessage {
|
||||
scalar: [display_name, old_display_name],
|
||||
array: [],
|
||||
bytes: []
|
||||
} => serialize_display_name_update, deserialize_display_name_update);
|
||||
map_message!(SimConsoleResponseMessage {
|
||||
scalar: [body => "body"],
|
||||
array: [],
|
||||
bytes: []
|
||||
});
|
||||
custom_message!(TeleportFailedMessage {
|
||||
scalar: [agent_id, extra_params, message_key, reason],
|
||||
array: [],
|
||||
@@ -982,6 +1783,16 @@ custom_message!(ViewerStatsMessage {
|
||||
array: [],
|
||||
bytes: []
|
||||
} => serialize_viewer_stats, deserialize_viewer_stats);
|
||||
custom_message!(UploaderRequestUpload {
|
||||
scalar: [url],
|
||||
array: [],
|
||||
bytes: []
|
||||
} => serialize_uploader_upload, deserialize_uploader_upload);
|
||||
custom_message!(UploaderRequestComplete {
|
||||
scalar: [asset_id],
|
||||
array: [],
|
||||
bytes: []
|
||||
} => serialize_uploader_complete, deserialize_uploader_complete);
|
||||
|
||||
fn serialize_agent_group(value: &AgentGroupDataUpdateMessage) -> Result<OSDMap, Error> {
|
||||
finish_map([
|
||||
|
||||
Reference in New Issue
Block a user