feat: stabilize OpenSim interactions and landmarks
This commit is contained in:
@@ -32,7 +32,7 @@ use crate::{
|
||||
};
|
||||
use libremetaverse_structured_data::{OSD, OSDMap, OSDParser};
|
||||
use libremetaverse_types::compat::{EventHandler, Subscription, Uri};
|
||||
use libremetaverse_types::{Color4, UUID, Vector3, Vector3d, Vector4};
|
||||
use libremetaverse_types::{Color4, UUID, Utils, Vector3, Vector3d, Vector4};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::fmt;
|
||||
use std::panic::{AssertUnwindSafe, catch_unwind};
|
||||
@@ -82,6 +82,13 @@ fn send_simulator_packet(
|
||||
simulator.native_send_packet_data(data, length, packet_type, zerocoded)
|
||||
}
|
||||
|
||||
fn open_sim_im_wire(mut data: Vec<u8>) -> Result<Vec<u8>, Error> {
|
||||
// OpenSim's own IM sender deliberately disables protocol zerocoding.
|
||||
let flags = data.first_mut().ok_or(Error::Argument)?;
|
||||
*flags &= !crate::Helpers::MSG_ZEROCODED;
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
fn parse_mute_list(text: &str) -> HashMap<String, MuteEntry> {
|
||||
let mut result = HashMap::new();
|
||||
for line in text.lines().map(str::trim).filter(|line| !line.is_empty()) {
|
||||
@@ -2331,10 +2338,10 @@ impl AgentManager {
|
||||
packet.agent_data.agent_id = self.native_agent_id();
|
||||
packet.agent_data.session_id = self.native_session_id();
|
||||
packet.message_block.dialog = dialog as u8;
|
||||
packet.message_block.from_agent_name = from_name.as_bytes().to_vec();
|
||||
packet.message_block.from_agent_name = Utils::string_to_bytes(from_name.clone())?;
|
||||
packet.message_block.from_group = false;
|
||||
packet.message_block.id = im_session_id;
|
||||
packet.message_block.message = message.into_bytes();
|
||||
packet.message_block.message = Utils::string_to_bytes(message)?;
|
||||
packet.message_block.offline = offline as u8;
|
||||
packet.message_block.to_agent_id = target;
|
||||
packet
|
||||
@@ -2346,7 +2353,7 @@ impl AgentManager {
|
||||
packet.message_block.region_id = region_id;
|
||||
self.send_packet(
|
||||
crate::packets::PacketType::ImprovedInstantMessage,
|
||||
packet.to_bytes_with_method()?,
|
||||
open_sim_im_wire(packet.to_bytes_with_method()?)?,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
@@ -4767,6 +4774,39 @@ mod tests {
|
||||
encoded
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn open_sim_im_wire_uses_legacy_unencoded_layout() {
|
||||
let packet = crate::packets::ImprovedInstantMessagePacket::new_with_constructor().unwrap();
|
||||
let generated = packet.to_bytes_with_method().unwrap();
|
||||
let wire = open_sim_im_wire(generated.clone()).unwrap();
|
||||
|
||||
assert_eq!(wire.len(), generated.len());
|
||||
assert_eq!(wire[0] & crate::Helpers::MSG_ZEROCODED, 0);
|
||||
assert_eq!(&wire[wire.len() - 5..], &[0, 0, 0, 0, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn open_sim_im_without_metadata_count_reaches_subscribers() {
|
||||
let client = GridClient::new().unwrap();
|
||||
let inner = AgentManagerInner::default();
|
||||
let received = Arc::new(Mutex::new(Vec::new()));
|
||||
let values = Arc::clone(&received);
|
||||
let _subscription = inner.instant_message.subscribe(Arc::new(move |event| {
|
||||
values.lock().unwrap().push(event.im().message);
|
||||
}));
|
||||
let mut packet =
|
||||
crate::packets::ImprovedInstantMessagePacket::new_with_constructor().unwrap();
|
||||
packet.message_block.dialog = InstantMessageDialog::MessageFromAgent as u8;
|
||||
packet.message_block.message = b"legacy OpenSim IM\0".to_vec();
|
||||
let mut wire = open_sim_im_wire(packet.to_bytes_with_method().unwrap()).unwrap();
|
||||
assert_eq!(wire.pop(), Some(0));
|
||||
|
||||
inner
|
||||
.handle_instant_message_packet(&wire, simulator(&client, loopback(13000)))
|
||||
.unwrap();
|
||||
assert_eq!(*received.lock().unwrap(), ["legacy OpenSim IM"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chat_uses_utf8_safe_protocol_chunks_and_exact_identity() {
|
||||
let client = Arc::new(GridClient::new().expect("client"));
|
||||
|
||||
@@ -21,8 +21,8 @@ use crate::packets::{
|
||||
use crate::{
|
||||
AgentFlags, AgentManagerControlFlags, AgentManagerCrossingFailureReason,
|
||||
AgentManagerCrossingState, AgentState, BorderCrossingDirection, Error, GridClient,
|
||||
InstantMessageDialog, InstantMessageOnline, NetworkManager, Simulator, TeleportFlags,
|
||||
TeleportStatus,
|
||||
GridLayerType, InstantMessageDialog, InstantMessageOnline, NetworkManager, Simulator,
|
||||
TeleportFlags, TeleportStatus,
|
||||
};
|
||||
use libremetaverse_types::compat::{CancellationToken, EventHandler, Subscription, Uri};
|
||||
use libremetaverse_types::{Quaternion, UUID, Vector3, Vector3d, Vector4};
|
||||
@@ -2779,7 +2779,7 @@ impl AgentManager {
|
||||
);
|
||||
return Ok(false);
|
||||
};
|
||||
let handle = if current.name.eq_ignore_ascii_case(&sim_name) {
|
||||
let connected_handle = if current.name.eq_ignore_ascii_case(&sim_name) {
|
||||
Some(current.handle)
|
||||
} else {
|
||||
self.client
|
||||
@@ -2790,6 +2790,20 @@ impl AgentManager {
|
||||
.find(|simulator| simulator.name.eq_ignore_ascii_case(&sim_name))
|
||||
.map(|simulator| simulator.handle)
|
||||
};
|
||||
let handle = if let Some(handle) = connected_handle {
|
||||
Some(handle)
|
||||
} else {
|
||||
self.client
|
||||
.grid()
|
||||
.get_grid_region_with_string_grid_layer_type_cancellation_token(
|
||||
sim_name.clone(),
|
||||
GridLayerType::Objects,
|
||||
cancellation_token.clone(),
|
||||
)
|
||||
.await?
|
||||
.flatten()
|
||||
.map(|region| region.region_handle)
|
||||
};
|
||||
let Some(handle) = handle else {
|
||||
self.movement.runtime.teleport_event(
|
||||
format!("Unable to resolve simulator named: {sim_name}"),
|
||||
|
||||
@@ -570,7 +570,7 @@ impl GridManager {
|
||||
p.agent_data.agent_id = agent;
|
||||
p.agent_data.session_id = session;
|
||||
p.agent_data.flags = layer as u32;
|
||||
p.name_data.name = Utils::string_to_bytes(name.to_owned())?;
|
||||
p.name_data.name = Utils::string_to_bytes(name.to_ascii_lowercase())?;
|
||||
self.send(&p, PacketType::MapNameRequest)
|
||||
}
|
||||
pub fn request_map_items(
|
||||
|
||||
@@ -3393,11 +3393,14 @@ impl InventoryManager {
|
||||
packet_type: PacketType,
|
||||
bytes: Vec<u8>,
|
||||
) -> Result<(), Error> {
|
||||
let zerocoded = bytes
|
||||
.first()
|
||||
.is_some_and(|flags| flags & crate::Helpers::MSG_ZEROCODED != 0);
|
||||
simulator.native_send_packet_data(
|
||||
bytes.clone(),
|
||||
i32::try_from(bytes.len()).map_err(|_| Error::Argument)?,
|
||||
packet_type,
|
||||
false,
|
||||
zerocoded,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -149272,7 +149272,11 @@ impl GeneratedPacket for crate::packets::ImprovedInstantMessagePacket {
|
||||
GeneratedBlock::decode_payload(&mut self.agent_data, reader)?;
|
||||
GeneratedBlock::decode_payload(&mut self.message_block, reader)?;
|
||||
GeneratedBlock::decode_payload(&mut self.estate_block, reader)?;
|
||||
let meta_data_count = usize::from(reader.read_u8()?);
|
||||
let meta_data_count = if reader.is_empty() {
|
||||
0
|
||||
} else {
|
||||
usize::from(reader.read_u8()?)
|
||||
};
|
||||
let mut meta_data = Vec::new();
|
||||
meta_data
|
||||
.try_reserve_exact(meta_data_count)
|
||||
|
||||
@@ -61,6 +61,10 @@ impl<'a> WireReader<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) const fn is_empty(&self) -> bool {
|
||||
self.position == self.end
|
||||
}
|
||||
|
||||
pub(crate) fn read_u8(&mut self) -> Result<u8, Error> {
|
||||
Ok(self.take(1, "truncated u8")?[0])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user