Complete first release candidate audit (#107)
Some checks failed
API and SemVer surface / api-surface (push) Failing after 1m34s
Native code generation / deterministic (push) Has been cancelled
Concurrency and resource soak audit / soak (push) Has been cancelled
Documentation / documentation (push) Has been cancelled
performance evidence / audit (push) Has been cancelled
First release candidate / non-fuzz-release-gate (push) Has been cancelled
Release platform and feature matrix / audit (push) Has been cancelled
Release platform and feature matrix / matrix (false, linux-stable-minimal, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, macos-stable-portable, x86_64-apple-darwin, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, windows-stable-portable, x86_64-pc-windows-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-msrv-portable, x86_64-unknown-linux-gnu, 1.96.0) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-default, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-features, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-release-surface, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Dependency and supply-chain audit / audit (push) Has been cancelled
Native release artifact audit / audit (push) Has been cancelled
Imaging and meshing gate / native (push) Failing after 53s
JPEG 2000 feature / linux (push) Successful in 2m49s
Native Rust workspace compile / compile (push) Failing after 59s
Skia feature / linux (push) Has been cancelled

This commit is contained in:
2026-08-12 14:44:28 +00:00
parent dceb394378
commit c9a1170a27
140 changed files with 82175 additions and 27179 deletions

View File

@@ -17,12 +17,13 @@ use crate::messages::linden::{
};
use crate::{
AgentAccessEventArgs, AgentDataReplyEventArgs, AgentExperiencesEventArgs,
AgentPreferencesEventArgs, AnimationsChangedEventArgs, AvatarRenderInfoEventArgs,
BalanceEventArgs, ChatEventArgs, ChatSessionMemberAddedEventArgs,
AgentPreferencesEventArgs, AlertMessageEventArgs, AnimationsChangedEventArgs,
AvatarRenderInfoEventArgs, BalanceEventArgs, ChatEventArgs, ChatSessionMemberAddedEventArgs,
ChatSessionMemberLeftEventArgs, ExperienceInfoEventArgs, ExperiencePreferencesEventArgs,
GroupChatJoinedEventArgs, MoneyBalanceReplyEventArgs, NavMeshStatusUpdateEventArgs,
ProductInfoEventArgs, RegionExperiencesEventArgs, SetDisplayNameReplyEventArgs,
ViewerBenefitsEventArgs,
GroupChatJoinedEventArgs, LoadUrlEventArgs, MeanCollisionEventArgs, MoneyBalanceReplyEventArgs,
NavMeshStatusUpdateEventArgs, ProductInfoEventArgs, RegionExperiencesEventArgs,
ScriptControlEventArgs, ScriptDialogEventArgs, ScriptQuestionEventArgs,
ScriptSensorReplyEventArgs, SetDisplayNameReplyEventArgs, ViewerBenefitsEventArgs,
};
use crate::{
AgentManagerAgentMovement, ChatSessionMember, ChatType, EffectType, Error, GridClient,
@@ -31,11 +32,11 @@ use crate::{
};
use libremetaverse_structured_data::{OSD, OSDMap, OSDParser};
use libremetaverse_types::compat::{EventHandler, Subscription, Uri};
use libremetaverse_types::{Color4, UUID, Vector3, Vector3d};
use libremetaverse_types::{Color4, UUID, Vector3, Vector3d, Vector4};
use std::collections::{BTreeMap, HashMap};
use std::fmt;
use std::panic::{AssertUnwindSafe, catch_unwind};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering};
use std::sync::{Arc, Mutex, OnceLock, RwLock};
use std::time::{Duration, Instant, UNIX_EPOCH};
@@ -317,8 +318,17 @@ pub(crate) struct AgentManagerInner {
disposed: AtomicBool,
agent_id: RwLock<UUID>,
session_id: RwLock<UUID>,
secure_session_id: RwLock<UUID>,
first_name: RwLock<String>,
last_name: RwLock<String>,
start_location: RwLock<String>,
look_at: RwLock<Vector3>,
home_region_handle: RwLock<u64>,
home_look_at: RwLock<Vector3>,
local_id: RwLock<u32>,
collision_plane: RwLock<Vector4>,
health: RwLock<f32>,
height_width_generation: AtomicU32,
active_group: RwLock<UUID>,
active_group_powers: RwLock<crate::GroupPowers>,
agent_access: RwLock<String>,
@@ -344,6 +354,7 @@ pub(crate) struct AgentManagerInner {
network: Mutex<Option<crate::NetworkManager>>,
raw_packet_subscription: Mutex<Option<Subscription>>,
caps_subscriptions: Mutex<Vec<Subscription>>,
login_callback: Mutex<Option<crate::NetworkManagerLoginResponseCallback>>,
movement: OnceLock<Arc<crate::agent_movement::AgentMovementRuntime>>,
chat_from_simulator: EventRegistry<ChatEventArgs>,
money_balance: EventRegistry<BalanceEventArgs>,
@@ -362,6 +373,13 @@ pub(crate) struct AgentManagerInner {
set_display_name_reply: EventRegistry<SetDisplayNameReplyEventArgs>,
nav_mesh_status_update: EventRegistry<NavMeshStatusUpdateEventArgs>,
mute_list_updated: EventRegistry<()>,
alert_message: EventRegistry<AlertMessageEventArgs>,
load_url: EventRegistry<LoadUrlEventArgs>,
mean_collision: EventRegistry<MeanCollisionEventArgs>,
script_control_change: EventRegistry<ScriptControlEventArgs>,
script_dialog: EventRegistry<ScriptDialogEventArgs>,
script_question: EventRegistry<ScriptQuestionEventArgs>,
script_sensor_reply: EventRegistry<ScriptSensorReplyEventArgs>,
}
impl Default for AgentManagerInner {
@@ -370,8 +388,17 @@ impl Default for AgentManagerInner {
disposed: AtomicBool::new(false),
agent_id: RwLock::new(UUID::zero()),
session_id: RwLock::new(UUID::zero()),
secure_session_id: RwLock::new(UUID::zero()),
first_name: RwLock::new(String::new()),
last_name: RwLock::new(String::new()),
start_location: RwLock::new(String::new()),
look_at: RwLock::new(Vector3::zero()),
home_region_handle: RwLock::new(0),
home_look_at: RwLock::new(Vector3::zero()),
local_id: RwLock::new(0),
collision_plane: RwLock::new(Vector4::zero()),
health: RwLock::new(0.0),
height_width_generation: AtomicU32::new(0),
active_group: RwLock::new(UUID::zero()),
active_group_powers: RwLock::new(crate::GroupPowers(0)),
agent_access: RwLock::new(String::new()),
@@ -397,6 +424,7 @@ impl Default for AgentManagerInner {
network: Mutex::new(None),
raw_packet_subscription: Mutex::new(None),
caps_subscriptions: Mutex::new(Vec::new()),
login_callback: Mutex::new(None),
movement: OnceLock::new(),
chat_from_simulator: EventRegistry::default(),
money_balance: EventRegistry::default(),
@@ -415,6 +443,13 @@ impl Default for AgentManagerInner {
set_display_name_reply: EventRegistry::default(),
nav_mesh_status_update: EventRegistry::default(),
mute_list_updated: EventRegistry::default(),
alert_message: EventRegistry::default(),
load_url: EventRegistry::default(),
mean_collision: EventRegistry::default(),
script_control_change: EventRegistry::default(),
script_dialog: EventRegistry::default(),
script_question: EventRegistry::default(),
script_sensor_reply: EventRegistry::default(),
}
}
}
@@ -444,6 +479,31 @@ impl AgentManagerInner {
crate::packets::PacketType::AvatarAnimation => {
self.handle_avatar_animation_packet(&event.data)
}
crate::packets::PacketType::AlertMessage => {
self.handle_alert_message_packet(&event.data)
}
crate::packets::PacketType::AgentAlertMessage => {
self.handle_agent_alert_message_packet(&event.data)
}
crate::packets::PacketType::HealthMessage => {
self.handle_health_message_packet(&event.data)
}
crate::packets::PacketType::LoadURL => self.handle_load_url_packet(&event.data),
crate::packets::PacketType::MeanCollisionAlert => {
self.handle_mean_collision_packet(&event.data)
}
crate::packets::PacketType::ScriptControlChange => {
self.handle_script_control_packet(&event.data)
}
crate::packets::PacketType::ScriptDialog => {
self.handle_script_dialog_packet(&event.data)
}
crate::packets::PacketType::ScriptQuestion => {
self.handle_script_question_packet(&event.data, event.simulator)
}
crate::packets::PacketType::ScriptSensorReply => {
self.handle_script_sensor_reply_packet(&event.data)
}
crate::packets::PacketType::MuteListUpdate => {
self.handle_mute_list_update_packet(&event.data, event.simulator)
}
@@ -458,6 +518,164 @@ impl AgentManagerInner {
let _ = result;
}
fn handle_alert_message_packet(&self, data: &[u8]) -> Result<(), Error> {
let packet = decode_raw_packet::<crate::packets::AlertMessagePacket>(data)?;
let (notification_id, extra_params) =
packet
.alert_info
.first()
.map_or(Ok::<_, Error>((None, None)), |info| {
let extra_params = if info.extra_params.is_empty() {
None
} else {
match OSDParser::deserialize_with_bytes(info.extra_params.clone())? {
OSD::Map(map) => Some(OSDMap::new_with_dictionary(map)?),
_ => return Err(Error::InvalidOperation),
}
};
Ok((Some(wire_string(info.message.clone())), extra_params))
})?;
self.alert_message.emit(AlertMessageEventArgs::new(
wire_string(packet.alert_data.message),
notification_id,
extra_params,
)?);
Ok(())
}
fn handle_agent_alert_message_packet(&self, data: &[u8]) -> Result<(), Error> {
let packet = decode_raw_packet::<crate::packets::AgentAlertMessagePacket>(data)?;
self.alert_message.emit(AlertMessageEventArgs::new(
wire_string(packet.alert_data.message),
None,
None,
)?);
Ok(())
}
fn handle_health_message_packet(&self, data: &[u8]) -> Result<(), Error> {
let packet = decode_raw_packet::<crate::packets::HealthMessagePacket>(data)?;
*self
.health
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = packet.health_data.health;
Ok(())
}
fn handle_load_url_packet(&self, data: &[u8]) -> Result<(), Error> {
let packet = decode_raw_packet::<crate::packets::LoadURLPacket>(data)?;
let data = packet.data;
self.load_url.emit(LoadUrlEventArgs::new(
wire_string(data.object_name),
data.object_id,
data.owner_id,
data.owner_is_group,
wire_string(data.message),
wire_string(data.url),
)?);
Ok(())
}
fn handle_mean_collision_packet(&self, data: &[u8]) -> Result<(), Error> {
let packet = decode_raw_packet::<crate::packets::MeanCollisionAlertPacket>(data)?;
for collision in packet.mean_collision {
let type_ = match collision.type_ {
0 => crate::MeanCollisionType::None,
1 => crate::MeanCollisionType::Bump,
2 => crate::MeanCollisionType::LLPushObject,
3 => crate::MeanCollisionType::SelectedObjectCollide,
4 => crate::MeanCollisionType::ScriptedObjectCollide,
5 => crate::MeanCollisionType::PhysicalObjectCollide,
_ => return Err(Error::Argument),
};
self.mean_collision.emit(MeanCollisionEventArgs::new(
type_,
collision.perp,
collision.victim,
collision.mag,
UNIX_EPOCH + Duration::from_secs(u64::from(collision.time)),
)?);
}
Ok(())
}
fn handle_script_control_packet(&self, data: &[u8]) -> Result<(), Error> {
let packet = decode_raw_packet::<crate::packets::ScriptControlChangePacket>(data)?;
for change in packet.data {
self.script_control_change.emit(ScriptControlEventArgs::new(
crate::ScriptControlChange(change.controls),
change.pass_to_agent,
change.take_controls,
)?);
}
Ok(())
}
fn handle_script_dialog_packet(&self, data: &[u8]) -> Result<(), Error> {
let packet = decode_raw_packet::<crate::packets::ScriptDialogPacket>(data)?;
let owner_id = packet
.owner_data
.first()
.map_or(UUID::zero(), |owner| owner.owner_id);
let buttons = packet
.buttons
.into_iter()
.map(|button| wire_string(button.button_label))
.collect();
let dialog = packet.data;
self.script_dialog.emit(ScriptDialogEventArgs::new(
wire_string(dialog.message),
wire_string(dialog.object_name),
dialog.image_id,
dialog.object_id,
wire_string(dialog.first_name),
wire_string(dialog.last_name),
dialog.chat_channel,
buttons,
owner_id,
)?);
Ok(())
}
fn handle_script_question_packet(
&self,
data: &[u8],
simulator: Simulator,
) -> Result<(), Error> {
let packet = decode_raw_packet::<crate::packets::ScriptQuestionPacket>(data)?;
let question = packet.data;
self.script_question.emit(ScriptQuestionEventArgs::new(
simulator,
question.task_id,
question.item_id,
wire_string(question.object_name),
wire_string(question.object_owner),
crate::ScriptPermission(question.questions),
)?);
Ok(())
}
fn handle_script_sensor_reply_packet(&self, data: &[u8]) -> Result<(), Error> {
let packet = decode_raw_packet::<crate::packets::ScriptSensorReplyPacket>(data)?;
let requester_id = packet.requester.source_id;
for sensed in packet.sensed_data {
self.script_sensor_reply
.emit(ScriptSensorReplyEventArgs::new(
requester_id,
sensed.group_id,
wire_string(sensed.name),
sensed.object_id,
sensed.owner_id,
sensed.position,
sensed.range,
sensed.rotation,
crate::ScriptSensorTypeFlags(sensed.type_),
sensed.velocity,
)?);
}
Ok(())
}
fn handle_mute_list_update_packet(
&self,
data: &[u8],
@@ -747,6 +965,81 @@ impl AgentManagerInner {
)
}
fn apply_login_response(&self, response: &crate::LoginResponseData) {
*self
.agent_id
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = response.agent_id();
*self
.session_id
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = response.session_id();
*self
.secure_session_id
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = response.secure_session_id();
*self
.first_name
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = response.first_name();
*self
.last_name
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = response.last_name();
*self
.start_location
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = response.start_location();
*self
.agent_access
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = response.agent_access();
let look_at = response.look_at();
*self
.look_at
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = look_at;
let home = response.home();
*self
.home_region_handle
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = home.region_handle;
*self
.home_look_at
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = home.look_at;
*self
.benefits
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = response.account_level_benefits();
*self
.active_gestures
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = response.gestures();
if let Some(movement) = self.movement.get() {
movement.set_home_position(home.position);
let _ = movement.set_login_look_at(look_at);
}
}
pub(crate) fn update_avatar_object_state(&self, local_id: u32, collision_plane: Vector4) {
*self
.local_id
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = local_id;
*self
.collision_plane
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = collision_plane;
}
pub(crate) fn avatar_local_id(&self) -> u32 {
*self
.local_id
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
fn handle_caps_event(
self: &Arc<Self>,
client: Arc<GridClient>,
@@ -1044,6 +1337,39 @@ impl AgentManager {
Arc::downgrade(&inner),
)
}));
{
let mut callback = mutex(&inner.login_callback);
if callback.is_none() {
let weak = Arc::downgrade(&inner);
let registered = crate::NetworkManagerLoginResponseCallback::from_handler(
move |success, redirect, _message, _reason, response| {
if success
&& !redirect
&& let (Some(inner), Some(response)) = (weak.upgrade(), response)
{
inner.apply_login_response(&response);
}
},
);
network.native_register_login_response_callback(
registered.clone(),
Some(vec![
"agent_id".into(),
"session_id".into(),
"secure_session_id".into(),
"first_name".into(),
"last_name".into(),
"start_location".into(),
"agent_access".into(),
"look_at".into(),
"home".into(),
"gestures".into(),
"account_level_benefits".into(),
]),
)?;
*callback = Some(registered);
}
}
Ok(Self {
agent_state_status: None,
group_chat_sessions: Arc::clone(&inner.group_chat_sessions),
@@ -1076,12 +1402,66 @@ impl AgentManager {
self.inner.animations_changed.subscribe(handler)
}
pub(crate) fn native_subscribe_alert_message(
&self,
handler: EventHandler<AlertMessageEventArgs>,
) -> Subscription {
self.inner.alert_message.subscribe(handler)
}
pub(crate) fn native_subscribe_load_url(
&self,
handler: EventHandler<LoadUrlEventArgs>,
) -> Subscription {
self.inner.load_url.subscribe(handler)
}
pub(crate) fn native_subscribe_mean_collision(
&self,
handler: EventHandler<MeanCollisionEventArgs>,
) -> Subscription {
self.inner.mean_collision.subscribe(handler)
}
pub(crate) fn native_subscribe_script_control_change(
&self,
handler: EventHandler<ScriptControlEventArgs>,
) -> Subscription {
self.inner.script_control_change.subscribe(handler)
}
pub(crate) fn native_subscribe_script_dialog(
&self,
handler: EventHandler<ScriptDialogEventArgs>,
) -> Subscription {
self.inner.script_dialog.subscribe(handler)
}
pub(crate) fn native_subscribe_script_question(
&self,
handler: EventHandler<ScriptQuestionEventArgs>,
) -> Subscription {
self.inner.script_question.subscribe(handler)
}
pub(crate) fn native_subscribe_script_sensor_reply(
&self,
handler: EventHandler<ScriptSensorReplyEventArgs>,
) -> Subscription {
self.inner.script_sensor_reply.subscribe(handler)
}
pub(crate) fn native_dispose(&self) -> Result<(), Error> {
if self.inner.disposed.swap(true, Ordering::AcqRel) {
return Ok(());
}
mutex(&self.inner.raw_packet_subscription).take();
mutex(&self.inner.caps_subscriptions).clear();
if let Some(callback) = mutex(&self.inner.login_callback).take()
&& let Some(network) = mutex(&self.inner.network).as_ref()
{
network.native_unregister_login_response_callback(&callback)?;
}
if let Some(movement) = self.inner.movement.get() {
movement.stop();
}
@@ -1388,6 +1768,297 @@ impl AgentManager {
.unwrap_or_else(std::sync::PoisonError::into_inner) = value;
}
pub(crate) fn native_secure_session_id(&self) -> UUID {
*self
.inner
.secure_session_id
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
pub(crate) fn native_set_secure_session_id(&self, value: UUID) {
*self
.inner
.secure_session_id
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = value;
}
pub(crate) fn native_start_location(&self) -> String {
self.inner
.start_location
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.clone()
}
pub(crate) fn native_set_start_location(&self, value: String) {
*self
.inner
.start_location
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = value;
}
pub(crate) fn native_look_at(&self) -> Vector3 {
*self
.inner
.look_at
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
pub(crate) fn native_set_look_at(&self, value: Vector3) {
*self
.inner
.look_at
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = value;
let _ = self
.inner
.movement
.get()
.map(|movement| movement.set_login_look_at(value));
}
pub(crate) fn native_home_region_handle(&self) -> u64 {
*self
.inner
.home_region_handle
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
pub(crate) fn native_home_look_at(&self) -> Vector3 {
*self
.inner
.home_look_at
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
pub(crate) fn native_local_id(&self) -> u32 {
*self
.inner
.local_id
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
pub(crate) fn native_collision_plane(&self) -> Vector4 {
*self
.inner
.collision_plane
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
pub(crate) fn native_health(&self) -> f32 {
*self
.inner
.health
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
pub(crate) fn native_set_health(&self, value: f32) {
*self
.inner
.health
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = value;
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn native_grab(
&self,
object_local_id: u32,
grab_offset: Vector3,
uv_coord: Vector3,
st_coord: Vector3,
face_index: i32,
position: Vector3,
normal: Vector3,
binormal: Vector3,
) -> Result<(), Error> {
let mut packet = crate::packets::ObjectGrabPacket::new_with_constructor()?;
packet.agent_data.agent_id = self.native_agent_id();
packet.agent_data.session_id = self.native_session_id();
packet.object_data.local_id = object_local_id;
packet.object_data.grab_offset = grab_offset;
let mut surface = crate::packets::ObjectGrabPacketSurfaceInfoBlock::new_with_constructor()?;
surface.uv_coord = uv_coord;
surface.st_coord = st_coord;
surface.face_index = face_index;
surface.position = position;
surface.normal = normal;
surface.binormal = binormal;
packet.surface_info = vec![surface];
self.send_current_packet(
crate::packets::PacketType::ObjectGrab,
packet.to_bytes_with_method()?,
)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn native_grab_update(
&self,
object_id: UUID,
grab_position: Vector3,
grab_offset: Vector3,
uv_coord: Vector3,
st_coord: Vector3,
face_index: i32,
position: Vector3,
normal: Vector3,
binormal: Vector3,
) -> Result<(), Error> {
let mut packet = crate::packets::ObjectGrabUpdatePacket::new_with_constructor()?;
packet.agent_data.agent_id = self.native_agent_id();
packet.agent_data.session_id = self.native_session_id();
packet.object_data.object_id = object_id;
packet.object_data.grab_offset_initial = grab_offset;
packet.object_data.grab_position = grab_position;
packet.object_data.time_since_last = 0;
let mut surface =
crate::packets::ObjectGrabUpdatePacketSurfaceInfoBlock::new_with_constructor()?;
surface.uv_coord = uv_coord;
surface.st_coord = st_coord;
surface.face_index = face_index;
surface.position = position;
surface.normal = normal;
surface.binormal = binormal;
packet.surface_info = vec![surface];
self.send_current_packet(
crate::packets::PacketType::ObjectGrabUpdate,
packet.to_bytes_with_method()?,
)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn native_de_grab(
&self,
object_local_id: u32,
uv_coord: Vector3,
st_coord: Vector3,
face_index: i32,
position: Vector3,
normal: Vector3,
binormal: Vector3,
) -> Result<(), Error> {
let mut packet = crate::packets::ObjectDeGrabPacket::new_with_constructor()?;
packet.agent_data.agent_id = self.native_agent_id();
packet.agent_data.session_id = self.native_session_id();
packet.object_data.local_id = object_local_id;
let mut surface =
crate::packets::ObjectDeGrabPacketSurfaceInfoBlock::new_with_constructor()?;
surface.uv_coord = uv_coord;
surface.st_coord = st_coord;
surface.face_index = face_index;
surface.position = position;
surface.normal = normal;
surface.binormal = binormal;
packet.surface_info = vec![surface];
self.send_current_packet(
crate::packets::PacketType::ObjectDeGrab,
packet.to_bytes_with_method()?,
)
}
fn send_current_packet(
&self,
packet_type: crate::packets::PacketType,
data: Vec<u8>,
) -> Result<(), Error> {
let simulator = self
.client
.network()
.native_current_sim()
.ok_or(Error::InvalidOperation)?;
send_simulator_packet(&simulator, packet_type, data)
}
pub(crate) fn native_set_height_width(&self, height: u16, width: u16) -> Result<(), Error> {
let mut packet = crate::packets::AgentHeightWidthPacket::new_with_constructor()?;
packet.agent_data.agent_id = self.native_agent_id();
packet.agent_data.session_id = self.native_session_id();
packet.agent_data.circuit_code = self.client.network().native_circuit_code();
packet.height_width_block.height = height;
packet.height_width_block.width = width;
packet.height_width_block.gen_counter = self
.inner
.height_width_generation
.fetch_add(1, Ordering::AcqRel);
self.send_current_packet(
crate::packets::PacketType::AgentHeightWidth,
packet.to_bytes_with_method()?,
)
}
pub(crate) fn native_set_home(&self) -> Result<(), Error> {
let mut packet = crate::packets::SetStartLocationRequestPacket::new_with_constructor()?;
packet.agent_data.agent_id = self.native_agent_id();
packet.agent_data.session_id = self.native_session_id();
packet.start_location_data.location_pos = self.native_movement_runtime().sim_position();
packet.start_location_data.location_id = 1;
packet.start_location_data.sim_name = Vec::new();
packet.start_location_data.location_look_at =
self.native_movement_runtime().camera_at_axis();
self.send_current_packet(
crate::packets::PacketType::SetStartLocationRequest,
packet.to_bytes_with_method()?,
)
}
pub(crate) fn native_script_question_reply(
&self,
simulator: Simulator,
item_id: UUID,
task_id: UUID,
permissions: crate::ScriptPermission,
) -> Result<(), Error> {
let mut packet = crate::packets::ScriptAnswerYesPacket::new_with_constructor()?;
packet.agent_data.agent_id = self.native_agent_id();
packet.agent_data.session_id = self.native_session_id();
packet.data.item_id = item_id;
packet.data.task_id = task_id;
packet.data.questions = permissions.0;
send_simulator_packet(
&simulator,
crate::packets::PacketType::ScriptAnswerYes,
packet.to_bytes_with_method()?,
)
}
pub(crate) fn native_request_script_sensor(
&self,
name: String,
search_id: UUID,
type_: crate::ScriptSensorTypeFlags,
range: f32,
arc: f32,
request_id: UUID,
simulator: Simulator,
) -> Result<(), Error> {
let mut packet = crate::packets::ScriptSensorRequestPacket::new_with_constructor()?;
packet.requester.arc = arc;
packet.requester.range = range;
packet.requester.region_handle = simulator.handle;
packet.requester.request_id = request_id;
packet.requester.search_dir = libremetaverse_types::Quaternion::identity();
packet.requester.search_id = search_id;
packet.requester.search_name = name.into_bytes();
packet.requester.search_pos = Vector3::zero();
packet.requester.search_regions = 0;
packet.requester.source_id = self.native_agent_id();
packet.requester.type_ = type_.0;
send_simulator_packet(
&simulator,
crate::packets::PacketType::ScriptSensorRequest,
packet.to_bytes_with_method()?,
)
}
pub(crate) fn native_last_name(&self) -> String {
self.inner
.last_name
@@ -4064,6 +4735,7 @@ fn osd_uuid_array_contains(map: &OSDMap, key: &str, id: UUID) -> Result<bool, Er
}
#[cfg(test)]
#[allow(clippy::too_many_lines)] // Protocol scenarios remain readable as contiguous exchanges.
mod tests {
use super::*;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};