fix(skins): better offline handling (#6344)

This commit is contained in:
Calum H.
2026-06-10 14:28:10 +01:00
committed by GitHub
parent b828fa17de
commit 180cef6eaa
5 changed files with 237 additions and 65 deletions
+72 -45
View File
@@ -268,12 +268,11 @@ pub async fn get_available_capes() -> crate::Result<Vec<Cape>> {
.await?
.ok_or(ErrorKind::NoCredentialsError)?;
let profile = selected_credentials
.online_profile_fresh()
.await
.ok_or_else(|| ErrorKind::OnlineMinecraftProfileUnavailable {
user_name: selected_credentials.offline_profile.name.clone(),
})?;
let Some(profile) = selected_credentials.online_profile_fresh().await
else {
return Ok(Vec::new());
};
let pending_skin_change = pending_effective_skin_change(profile.id).await;
let pending_cape_id = pending_skin_change
.as_ref()
@@ -309,16 +308,22 @@ pub async fn get_available_skins() -> crate::Result<Vec<Skin>> {
.await?
.ok_or(ErrorKind::NoCredentialsError)?;
let profile = selected_credentials
.online_profile_fresh()
.await
.ok_or_else(|| ErrorKind::OnlineMinecraftProfileUnavailable {
user_name: selected_credentials.offline_profile.name.clone(),
})?;
let online_profile = selected_credentials.online_profile_fresh().await;
let profile_id = online_profile
.as_ref()
.map_or(selected_credentials.offline_profile.id, |profile| {
profile.id
});
let current_skin = profile.current_skin()?;
let current_cape_id = profile.current_cape().map(|cape| cape.id);
let pending_skin_change = pending_effective_skin_change(profile.id).await;
let current_skin = online_profile
.as_ref()
.map(|profile| profile.current_skin())
.transpose()?;
let current_cape_id = online_profile
.as_ref()
.and_then(|profile| profile.current_cape())
.map(|cape| cape.id);
let pending_skin_change = pending_effective_skin_change(profile_id).await;
let pending_unequip = pending_skin_change
.as_ref()
.is_some_and(PendingEffectiveSkinChange::is_unequip);
@@ -326,16 +331,15 @@ pub async fn get_available_skins() -> crate::Result<Vec<Skin>> {
.as_ref()
.and_then(PendingEffectiveSkinChange::skin);
let fallback_default_skin = assets::DEFAULT_SKINS.first();
let fallback_default_skin = get_fallback_default_skin()?;
let current_skin_texture_key = pending_skin.as_ref().map_or_else(
|| {
if pending_unequip {
fallback_default_skin.map_or_else(
|| current_skin.texture_key(),
|skin| Arc::clone(&skin.texture_key),
)
} else {
Arc::clone(&fallback_default_skin.texture_key)
} else if let Some(current_skin) = current_skin {
current_skin.texture_key()
} else {
Arc::clone(&fallback_default_skin.texture_key)
}
},
|skin| skin.texture_key.clone(),
@@ -343,10 +347,11 @@ pub async fn get_available_skins() -> crate::Result<Vec<Skin>> {
let current_skin_variant = pending_skin.as_ref().map_or_else(
|| {
if pending_unequip {
fallback_default_skin
.map_or(current_skin.variant, |skin| skin.variant)
} else {
fallback_default_skin.variant
} else if let Some(current_skin) = current_skin {
current_skin.variant
} else {
fallback_default_skin.variant
}
},
|skin| skin.variant,
@@ -354,8 +359,10 @@ pub async fn get_available_skins() -> crate::Result<Vec<Skin>> {
let current_cape_id = pending_skin.as_ref().map_or(
if pending_unequip {
None
} else {
} else if current_skin.is_some() {
current_cape_id
} else {
None
},
|skin| skin.cape_id,
);
@@ -364,38 +371,39 @@ pub async fn get_available_skins() -> crate::Result<Vec<Skin>> {
let mut custom_skins = Vec::new();
let mut saved_default_skins = Vec::new();
for mut custom_skin in CustomMinecraftSkin::get_all(profile.id, &state.pool)
for mut custom_skin in CustomMinecraftSkin::get_all(profile_id, &state.pool)
.await?
.collect::<Vec<_>>()
.await
{
let is_saved_default_skin =
is_bundled_skin(&custom_skin.texture_key, custom_skin.variant);
let current_skin_sync = if pending_skin.is_some() {
SavedSkinSync {
is_current_skin: custom_skin.texture_key
== current_skin_texture_key.as_ref()
&& custom_skin.variant == current_skin_variant
&& custom_skin.cape_id == current_cape_id,
settings_changed: false,
}
} else {
sync_saved_skin_with_current_profile(
&mut custom_skin,
&current_skin_texture_key,
current_skin_variant,
current_cape_id,
)
};
let current_skin_sync =
if pending_skin.is_some() || current_skin.is_none() {
SavedSkinSync {
is_current_skin: custom_skin.texture_key
== current_skin_texture_key.as_ref()
&& custom_skin.variant == current_skin_variant
&& custom_skin.cape_id == current_cape_id,
settings_changed: false,
}
} else {
sync_saved_skin_with_current_profile(
&mut custom_skin,
&current_skin_texture_key,
current_skin_variant,
current_cape_id,
)
};
let synced_texture_blob = if current_skin_sync.settings_changed {
let texture_blob = custom_skin.texture_blob(&state.pool).await?;
if is_saved_default_skin && custom_skin.cape_id.is_none() {
custom_skin.remove(profile.id, &state.pool).await?;
custom_skin.remove(profile_id, &state.pool).await?;
} else {
CustomMinecraftSkin::add(
profile.id,
profile_id,
&custom_skin.texture_key,
&texture_blob,
custom_skin.variant,
@@ -483,7 +491,7 @@ pub async fn get_available_skins() -> crate::Result<Vec<Skin>> {
if let Some(mut skin) = pending_skin {
skin.is_equipped = true;
available_skins.push(skin);
} else {
} else if let Some(current_skin) = current_skin {
available_skins.push(Skin {
texture_key: current_skin_texture_key,
name: current_skin.name.as_deref().map(Arc::from),
@@ -1247,6 +1255,25 @@ fn is_bundled_skin(texture_key: &str, variant: MinecraftSkinVariant) -> bool {
})
}
fn get_fallback_default_skin() -> crate::Result<&'static Skin> {
assets::DEFAULT_SKINS
.iter()
.find(|skin| {
skin.name.as_deref() == Some("Steve")
&& skin.variant == MinecraftSkinVariant::Classic
})
.or_else(|| {
assets::DEFAULT_SKINS
.iter()
.find(|skin| skin.name.as_deref() == Some("Steve"))
})
.or_else(|| assets::DEFAULT_SKINS.first())
.ok_or_else(|| {
ErrorKind::OtherError("No bundled default skins found".into())
.as_error()
})
}
fn local_skin_texture_key(texture_blob: &[u8]) -> Arc<str> {
Arc::from(format!("local-{:x}", sha2::Sha256::digest(texture_blob)))
}