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

@@ -159,6 +159,7 @@ pub(crate) struct AppearanceManagerInner {
appearance_cancel: Mutex<Option<CancellationTokenSource>>,
appearance_gate: Arc<tokio::sync::Semaphore>,
events: AppearanceEvents,
login_callback: Mutex<Option<crate::NetworkManagerLoginResponseCallback>>,
network_subscriptions: Mutex<Vec<Subscription>>,
}
@@ -232,11 +233,35 @@ fn handle_appearance_packet(
match packet_type {
PacketType::AgentWearablesUpdate => handle_wearables_update(inner, data),
PacketType::AgentCachedTextureResponse => handle_cached_bakes(inner, data),
PacketType::AvatarAppearance => handle_avatar_appearance(inner, data),
PacketType::RebakeAvatarTextures => handle_rebake_request(inner, data),
_ => {}
}
}
fn handle_avatar_appearance(inner: &Arc<AppearanceManagerInner>, data: Vec<u8>) {
let mut offset = 0;
let Ok(packet) =
crate::packets::AvatarAppearancePacket::new_with_bytes_int32(data, &mut offset)
else {
return;
};
let Some(client) = inner.client.upgrade() else {
return;
};
let Ok(network) = client.native_network() else {
return;
};
if packet.sender.id != network.native_agent_id() {
return;
}
if let Some(appearance) = packet.appearance_data.first() {
inner
.last_cof_version
.fetch_max(appearance.cof_version, Ordering::AcqRel);
}
}
fn handle_wearables_update(inner: &Arc<AppearanceManagerInner>, data: Vec<u8>) {
let mut offset = 0;
let Ok(packet) = AgentWearablesUpdatePacket::new_with_bytes_int32(data, &mut offset) else {
@@ -371,9 +396,31 @@ impl AppearanceManager {
appearance_cancel: Mutex::new(None),
appearance_gate: Arc::new(tokio::sync::Semaphore::new(1)),
events: AppearanceEvents::default(),
login_callback: Mutex::new(None),
network_subscriptions: Mutex::new(Vec::new()),
});
let weak = Arc::downgrade(&inner);
let login_callback = crate::NetworkManagerLoginResponseCallback::from_handler(
move |success, redirect, _message, _reason, response| {
if success
&& !redirect
&& let (Some(inner), Some(response)) = (weak.upgrade(), response)
{
inner.last_cof_version.fetch_max(
i32::try_from(response.cof_version()).unwrap_or(i32::MAX),
Ordering::AcqRel,
);
}
},
);
client
.native_network()?
.native_register_login_response_callback(
login_callback.clone(),
Some(vec!["cof_version".to_owned()]),
)?;
*mutex(&inner.login_callback) = Some(login_callback);
let weak = Arc::downgrade(&inner);
let subscription = client
.native_network()?
.subscribe_raw_packet(Arc::new(move |event| {
@@ -463,6 +510,12 @@ impl AppearanceManager {
#[allow(clippy::unnecessary_wraps)] // The mapped Dispose API is fallible.
pub(crate) fn native_dispose(&self) -> Result<(), Error> {
if let Some(callback) = mutex(&self.inner.login_callback).take()
&& let Some(client) = self.inner.client.upgrade()
&& let Ok(network) = client.native_network()
{
network.native_unregister_login_response_callback(&callback)?;
}
self.inner.disposed.store(true, Ordering::Release);
write(&self.inner.wearables).clear();
self.inner
@@ -1049,6 +1102,70 @@ impl AppearanceManager {
)))
}
pub(crate) async fn native_apply_archetype(
&self,
archetype: crate::GenepoolArchetype,
) -> Result<(), Error> {
let parameters = archetype.params();
{
let mut assets = write(&self.inner.wearable_assets);
for asset in assets.values_mut() {
for parameter in &parameters {
if let Some(value) = asset.params.get_mut(&parameter.id()) {
*value = parameter.value();
}
}
}
}
self.inner
.server_visual_parameters
.store(false, Ordering::Release);
self.native_request_set_appearance(true).await
}
pub(crate) async fn native_blend_to_archetype(
&self,
archetype: crate::GenepoolArchetype,
amount: f32,
) -> Result<(), Error> {
if !amount.is_finite() {
return Err(Error::Argument);
}
if amount <= 0.0 {
return Ok(());
}
let amount = amount.min(1.0);
let parameters = archetype.params();
{
let mut assets = write(&self.inner.wearable_assets);
for asset in assets.values_mut() {
for parameter in &parameters {
if let Some(current) = asset.params.get_mut(&parameter.id()) {
*current += amount * (parameter.value() - *current);
}
}
}
}
self.inner
.server_visual_parameters
.store(false, Ordering::Release);
self.native_request_set_appearance(true).await
}
pub(crate) async fn native_randomize_appearance(
&self,
random: Option<libremetaverse_types::compat::RandomSource>,
) -> Result<(), Error> {
let archetypes = crate::Genepool::archetypes();
let maximum = i32::try_from(archetypes.len()).map_err(|_| Error::IndexOutOfRange)?;
let index = random.unwrap_or_default().next(maximum)?;
let archetype = archetypes
.into_iter()
.nth(usize::try_from(index).map_err(|_| Error::IndexOutOfRange)?)
.ok_or(Error::IndexOutOfRange)?;
self.native_apply_archetype(archetype).await
}
pub(crate) fn native_server_baking_region(&self) -> Result<bool, Error> {
let protocols = self
.client()?
@@ -2303,7 +2420,7 @@ mod tests {
let delivered_handler = Arc::clone(&delivered);
let releasing = Arc::clone(&released);
let healthy = registry.subscribe(Arc::new(move |value| {
let _keep_alive = &releasing;
std::hint::black_box(&releasing);
delivered_handler.fetch_add(value as u64, Ordering::AcqRel);
}));
drop(released);
@@ -2555,6 +2672,40 @@ mod tests {
);
}
#[test]
fn self_avatar_appearance_advances_cof_version_monotonically() {
let client = GridClient::new().unwrap();
let manager = client.appearance();
let mut packet = crate::packets::AvatarAppearancePacket::new_with_constructor().unwrap();
packet.sender.id = client.network().native_agent_id();
let mut appearance =
crate::packets::AvatarAppearancePacketAppearanceDataBlock::new_with_constructor()
.unwrap();
appearance.cof_version = 17;
packet.appearance_data.push(appearance);
handle_appearance_packet(
&manager.inner,
PacketType::AvatarAppearance,
packet.to_bytes_with_method().unwrap(),
);
assert_eq!(manager.native_last_cof_version(), 17);
let mut older = crate::packets::AvatarAppearancePacket::new_with_constructor().unwrap();
older.sender.id = client.network().native_agent_id();
let mut older_data =
crate::packets::AvatarAppearancePacketAppearanceDataBlock::new_with_constructor()
.unwrap();
older_data.cof_version = 3;
older.appearance_data.push(older_data);
handle_appearance_packet(
&manager.inner,
PacketType::AvatarAppearance,
older.to_bytes_with_method().unwrap(),
);
assert_eq!(manager.native_last_cof_version(), 17);
}
#[tokio::test]
async fn failed_appearance_request_emits_failure_and_empty_cache_request_does_not_wait() {
let client = GridClient::new().unwrap();