diff --git a/packages/app-lib/src/api/minecraft_skins.rs b/packages/app-lib/src/api/minecraft_skins.rs index 58c71aa938..69483b99e2 100644 --- a/packages/app-lib/src/api/minecraft_skins.rs +++ b/packages/app-lib/src/api/minecraft_skins.rs @@ -361,6 +361,7 @@ pub async fn get_available_skins() -> crate::Result> { ); let mut found_equipped_skin = false; let mut available_skins = Vec::new(); + 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) @@ -426,7 +427,7 @@ pub async fn get_available_skins() -> crate::Result> { None => custom_skin.texture_blob(&state.pool).await?, }; - available_skins.push(Skin { + custom_skins.push(Skin { name: None, section: None, variant: custom_skin.variant, @@ -444,6 +445,9 @@ pub async fn get_available_skins() -> crate::Result> { }); } + custom_skins.sort_by(|a, b| a.texture.as_str().cmp(b.texture.as_str())); + available_skins.extend(custom_skins); + for default_skin in assets::DEFAULT_SKINS.iter() { let is_equipped = !found_equipped_skin && default_skin.texture_key == current_skin_texture_key @@ -676,9 +680,17 @@ async fn equip_skin_now( preserve_current_profile_skin(&state, &profile).await?; + let texture_blob = png_util::url_to_data_stream(&skin.texture) + .await? + .try_fold(Vec::new(), |mut texture, chunk| async move { + texture.extend_from_slice(&chunk); + Ok(texture) + }) + .await?; + let profile = mojang_api::MinecraftSkinOperation::equip( selected_credentials, - png_util::url_to_data_stream(&skin.texture).await?, + stream::iter([Ok::<_, String>(Bytes::from(texture_blob.clone()))]), skin.variant, ) .await?; @@ -693,6 +705,13 @@ async fn equip_skin_now( })?, }; + if let Err(error) = + persist_equipped_skin(&state, &profile, skin, &texture_blob).await + { + refresh_profile_cache(selected_credentials).await; + return Err(error); + } + if let Err(error) = sync_cape(selected_credentials, &profile, skin.cape_id).await { @@ -703,6 +722,54 @@ async fn equip_skin_now( Ok(()) } +async fn persist_equipped_skin( + state: &State, + profile: &MinecraftProfile, + skin: &Skin, + texture_blob: &[u8], +) -> crate::Result<()> { + let equipped_skin = profile.current_skin()?; + let equipped_skin_texture_key = equipped_skin.texture_key(); + let equipped_skin_variant = equipped_skin.variant; + let texture_key_changed = + skin.texture_key.as_ref() != equipped_skin_texture_key.as_ref(); + + if skin.cape_id.is_none() + && is_bundled_skin(&equipped_skin_texture_key, equipped_skin_variant) + { + CustomMinecraftSkin { + texture_key: equipped_skin_texture_key.to_string(), + variant: equipped_skin_variant, + cape_id: None, + } + .remove(profile.id, &state.pool) + .await?; + } else if texture_key_changed || !matches!(&skin.source, SkinSource::Custom) + { + CustomMinecraftSkin::add( + profile.id, + &equipped_skin_texture_key, + texture_blob, + equipped_skin_variant, + skin.cape_id, + &state.pool, + ) + .await?; + } + + if texture_key_changed { + CustomMinecraftSkin { + texture_key: skin.texture_key.to_string(), + variant: skin.variant, + cape_id: skin.cape_id, + } + .remove(profile.id, &state.pool) + .await?; + } + + Ok(()) +} + /// Removes a custom skin from the app database. /// /// The player will continue to be equipped with the same skin and cape as before, even if