feat(app/minecraft_skins): save current custom external skin when equipping skins

This commit is contained in:
Alejandro González
2025-05-25 20:29:33 +02:00
parent f50375220d
commit 0155262ddd

View File

@@ -102,7 +102,7 @@ impl Skin {
}
}
#[derive(Deserialize, Serialize, Debug)]
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SkinSource {
/// A default Minecraft skin, which may be assigned to players at random by default.
@@ -256,7 +256,9 @@ pub async fn get_available_skins() -> crate::Result<Vec<Skin>> {
}
/// Adds a custom skin to the app database and equips it for the currently selected
/// Minecraft profile.
/// Minecraft profile. If the currently equipped skin is custom but not managed by
/// the app (i.e., it was set externally by another launcher, the Minecraft website,
/// etc.), that skin will be added as a custom skin to the app database as well.
#[tracing::instrument]
pub async fn add_and_equip_custom_skin(
texture_blob: Bytes,
@@ -275,9 +277,11 @@ pub async fn add_and_equip_custom_skin(
.await?
.ok_or(ErrorKind::NoCredentialsError)?;
// We have to equip the skin first, as it's the Mojang API backend who knows
// how to compute the texture key we require, which we can then read from the
// updated player profile
save_current_custom_external_skin(&state, &selected_credentials).await?;
// We have to equip the new skin before storing it, as it's the Mojang API backend
// who knows how to compute the texture key we require, which we can then read from
// the updated player profile
mojang_api::MinecraftSkinOperation::equip(
&selected_credentials,
stream::iter([Ok::<_, String>(Bytes::clone(&texture_blob))]),
@@ -366,6 +370,10 @@ pub async fn set_default_cape(cape: Option<Cape>) -> crate::Result<()> {
///
/// This function does not check that the passed skin, if custom, exists in the app database,
/// giving the caller complete freedom to equip any skin at any time.
///
/// If the currently equipped skin is a custom skin that is not managed by the app (i.e., it was
/// set externally by another launcher, the Minecraft website, etc.), that skin will be added as
/// a custom skin to the app database, in order to allow the app to manage it later.
#[tracing::instrument]
pub async fn equip_skin(skin: Skin) -> crate::Result<()> {
let state = State::get().await?;
@@ -374,6 +382,8 @@ pub async fn equip_skin(skin: Skin) -> crate::Result<()> {
.await?
.ok_or(ErrorKind::NoCredentialsError)?;
save_current_custom_external_skin(&state, &selected_credentials).await?;
let profile =
selected_credentials.online_profile().await.ok_or_else(|| {
ErrorKind::OnlineMinecraftProfileUnavailable {
@@ -485,6 +495,39 @@ async fn sync_cape(
Ok(())
}
/// Stores the currently equipped skin as a custom skin, if it is a custom skin that is not
/// managed by the app (i.e., it was externally set).
async fn save_current_custom_external_skin(
state: &State,
selected_credentials: &Credentials,
) -> crate::Result<()> {
if let Some(current_external_skin) = get_available_skins()
.await?
.into_iter()
.find(|skin| skin.is_equipped)
.filter(|skin| skin.source == SkinSource::CustomExternal)
{
CustomMinecraftSkin::add(
selected_credentials.offline_profile.id,
&current_external_skin.texture_key,
&current_external_skin
.resolve_texture()
.await?
.try_fold(vec![], async |mut texture_blob, chunk| {
texture_blob.extend_from_slice(&chunk);
Ok(texture_blob)
})
.await?,
current_external_skin.variant,
current_external_skin.cape_id,
&state.pool,
)
.await?;
}
Ok(())
}
fn texture_blob_to_data_url(texture_blob: Vec<u8>) -> Arc<Url> {
let data = if is_png(&texture_blob) {
Cow::Owned(texture_blob)