Skip to content

Skin conversion fails #4

@SendableMetatype

Description

@SendableMetatype

Skin conversion fails for marketplace skins with missing texturewidth/textureheight in geometry

Summary

The native skin converter rejects valid marketplace skins whose geometry data does not include texturewidth and textureheight fields. The converter requires these fields explicitly, but they are optional in the Bedrock geometry format. When they are absent, the client falls back to SkinImageWidth/SkinImageHeight from client_data. Affected players appear as default skins to Java players.

Affected example

One example I came across is the Journey to the West skin pack: https://marketplace.minecraft.net/en-us/pdp?id=d0d42b2f-d0f8-4a2a-8870-493b674f55ac

The "Monkey King" skin is confirmed affected, along with multiple other skins in this pack. Any marketplace skin whose geometry omits texturewidth/textureheight will hit this issue, so more packs are likely affected.

root Cause

In native/skins/src/skin_convert/converter.rs, the get_correct_entry function requires texturewidth and textureheight to be present in the geometry data:

// Format version 1.8.0 path (line ~249):
let texture_width = geometry_data["texturewidth"].as_f64()
    .ok_or("geometry entry's texture width is not a number")?;

// Format version 1.12.0+ path (line ~270):
let texture_width = description["texture_width"].as_f64()
    .ok_or("geometry entry's texture width is not a number")?;

When these fields are absent, the conversion fails and the skin is rejected.

Example

The Monkey King skin's geometry (geometry.JourneyToTheWest.monkey, format version 1.8.0) contains only bones with no dimension fields:

{
   "format_version" : "1.8.0",
   "geometry.JourneyToTheWest.monkey" : {
      "bones" : [
         ...
      ]
   }
}

A working skin (e.g. geometry.BiomeSettlers01.TundraBrewerSlim) includes the fields:

{
   "format_version" : "1.8.0",
   "geometry.BiomeSettlers01.TundraBrewerSlim" : {
      "bones" : [ ... ],
      "textureheight" : 64,
      "texturewidth" : 64
   }
}

The actual skin image dimensions are always available in client_data as SkinImageWidth: 64 and SkinImageHeight: 64.

Expected Behavior

The converter should fall back to SkinImageWidth/SkinImageHeight from client_data when the geometry does not specify texture dimensions.

Microsoft's official geometry schema documentation confirms these fields are optional across all format versions:

  • geometry:1.8.0: int "texturewidth" : opt / int "textureheight" : opt (schema docs)
  • geometry:1.12.0: int "texture_width" : opt / int "texture_height" : opt (schema docs)
  • geometry:1.14.0+: Same as 1.12.0 (schema docs)

When absent, the Bedrock client uses the skin image dimensions as the UV coordinate space. The converter should do the same.

Proposed Fix

In get_correct_entry, fall back to client_data dimensions when the geometry field is missing:

let texture_width = geometry_data["texturewidth"].as_f64()
    .or_else(|| client_claims["SkinImageWidth"].as_f64())
    .ok_or("no texture width in geometry or client data")?;

let texture_height = geometry_data["textureheight"].as_f64()
    .or_else(|| client_claims["SkinImageHeight"].as_f64())
    .ok_or("no texture height in geometry or client data")?;

Same change needed for the 1.12.0+ format path with description["texture_width"] / description["texture_height"].

This requires threading client_claims through to get_correct_entry, which currently does not receive it.

Impac

Any Bedrock player using an affected marketplace skin will appear as a defautl skin to Java players when connecting through Geyser + Floodgate. The skin renders correctly in Bedrock. Only the Java conversion is affected. The Journey to the West pack is one known example, but any skin pack whose geometry omits these fields would be affected.

Extra info

Decoded SkinGeometryData from the Monkey King skin (SkinImageWidth: 64, SkinImageHeight: 64):

geometry.JourneyToTheWest.monkey (click to expand)
{
   "format_version" : "1.8.0",
   "geometry.JourneyToTheWest.monkey" : {
      "bones" : [
         {
            "cubes" : [
               {
                  "origin" : [ -4, 12, -2 ],
                  "size" : [ 8, 12, 4 ],
                  "uv" : [ 16, 16 ]
               }
            ],
            "name" : "body",
            "parent" : "waist",
            "pivot" : [ 0, 24, 0 ]
         },
         {
            "cubes" : [
               {
                  "origin" : [ -4, 24, -4 ],
                  "size" : [ 8, 8, 8 ],
                  "uv" : [ 0, 0 ]
               }
            ],
            "name" : "head",
            "parent" : "body",
            "pivot" : [ 0, 24, 0 ]
         },
         {
            "cubes" : [
               {
                  "inflate" : 0.50,
                  "origin" : [ -4, 24, -4 ],
                  "size" : [ 8, 8, 8 ],
                  "uv" : [ 32, 0 ]
               }
            ],
            "name" : "hat",
            "parent" : "head",
            "pivot" : [ 0, 24, 0 ]
         },
         {
            "cubes" : [
               {
                  "origin" : [ -2, 32, -1 ],
                  "size" : [ 4, 3, 4 ],
                  "uv" : [ 24, 1 ]
               },
               {
                  "origin" : [ -1, 28, 4 ],
                  "size" : [ 2, 2, 2 ],
                  "uv" : [ 36, 16 ]
               },
               {
                  "origin" : [ -1, 30, 4 ],
                  "size" : [ 2, 2, 2 ],
                  "uv" : [ 56, 4 ]
               },
               {
                  "origin" : [ -1, 32, 3 ],
                  "size" : [ 2, 1, 2 ],
                  "uv" : [ 12, 17 ]
               },
               {
                  "origin" : [ -1, 33, 3 ],
                  "size" : [ 2, 2, 2 ],
                  "uv" : [ 56, 0 ]
               },
               {
                  "origin" : [ -1, 35, 2 ],
                  "size" : [ 2, 2, 2 ],
                  "uv" : [ 0, 0 ]
               },
               {
                  "origin" : [ -1, 35, 1.421085000000000e-16 ],
                  "size" : [ 2, 2, 2 ],
                  "uv" : [ 0, 4 ]
               }
            ],
            "name" : "helmet",
            "parent" : "head",
            "pivot" : [ 0, 24, 1.421085000000000e-16 ]
         },
         {
            "cubes" : [
               {
                  "inflate" : 0.250,
                  "origin" : [ -4, 12, -2 ],
                  "size" : [ 8, 12, 4 ],
                  "uv" : [ 16, 32 ]
               }
            ],
            "name" : "jacket",
            "parent" : "body",
            "pivot" : [ 0, 24, 0 ]
         },
         {
            "cubes" : [
               {
                  "origin" : [ -8, 12, -2 ],
                  "size" : [ 4, 12, 4 ],
                  "uv" : [ 40, 16 ]
               }
            ],
            "name" : "rightArm",
            "parent" : "body",
            "pivot" : [ -5, 22, 0 ],
            "render_group_id" : 1
         },
         {
            "cubes" : [
               {
                  "inflate" : 0.250,
                  "origin" : [ -8, 12, -2 ],
                  "size" : [ 4, 12, 4 ],
                  "uv" : [ 40, 32 ]
               }
            ],
            "name" : "rightSleeve",
            "parent" : "rightArm",
            "pivot" : [ -5, 22, 0 ],
            "render_group_id" : 1
         },
         {
            "cubes" : [
               {
                  "origin" : [ 4.0014480, 12, -2 ],
                  "size" : [ 4, 12, 4 ],
                  "uv" : [ 32, 48 ]
               }
            ],
            "name" : "leftArm",
            "parent" : "body",
            "pivot" : [ 5.0014480, 22, 0 ],
            "render_group_id" : 1
         },
         {
            "cubes" : [
               {
                  "inflate" : 0.250,
                  "origin" : [ 4.0014480, 12, -2 ],
                  "size" : [ 4, 12, 4 ],
                  "uv" : [ 48, 48 ]
               }
            ],
            "name" : "leftSleeve",
            "parent" : "leftArm",
            "pivot" : [ 5.0014480, 22, 0 ],
            "render_group_id" : 1
         },
         {
            "cubes" : [
               { "origin" : [ -1, 13, 2 ], "size" : [ 1, 2, 1 ], "uv" : [ 0, 49 ] },
               { "origin" : [ 0, 21, 4 ], "size" : [ 2, 1, 1 ], "uv" : [ 36, 34 ] },
               { "origin" : [ -2, 21, 4 ], "size" : [ 2, 1, 1 ], "uv" : [ 12, 32 ] },
               { "origin" : [ -3, 20, 4 ], "size" : [ 2, 1, 1 ], "uv" : [ 12, 34 ] },
               { "origin" : [ -3, 18, 4 ], "size" : [ 1, 2, 1 ], "uv" : [ 32, 49 ] },
               { "origin" : [ -3, 17, 4 ], "size" : [ 2, 1, 1 ], "uv" : [ 44, 48 ] },
               { "origin" : [ -2, 16, 4 ], "size" : [ 1, 1, 1 ], "uv" : [ 16, 49 ] },
               { "origin" : [ -2, 16, 3 ], "size" : [ 2, 1, 1 ], "uv" : [ 44, 50 ] },
               { "origin" : [ 0, 15, 3 ], "size" : [ 1, 2, 1 ], "uv" : [ 28, 49 ] },
               { "origin" : [ 0, 14, 2 ], "size" : [ 1, 1, 2 ], "uv" : [ 12, 49 ] },
               { "origin" : [ -2, 22, 2 ], "size" : [ 1, 2, 1 ], "uv" : [ 60, 20 ] },
               { "origin" : [ -1, 20, 2 ], "size" : [ 1, 2, 1 ], "uv" : [ 60, 23 ] },
               { "origin" : [ 0, 18, 2 ], "size" : [ 1, 2, 1 ], "uv" : [ 60, 26 ] },
               { "origin" : [ 1, 16, 2 ], "size" : [ 1, 2, 1 ], "uv" : [ 60, 29 ] },
               { "origin" : [ 2, 14, 2 ], "size" : [ 1, 2, 1 ], "uv" : [ 60, 32 ] },
               { "origin" : [ 3, 12, 2 ], "size" : [ 1, 2, 1 ], "uv" : [ 60, 35 ] },
               { "origin" : [ 4, 10, 2 ], "size" : [ 1, 2, 1 ], "uv" : [ 60, 38 ] }
            ],
            "name" : "bodyArmor",
            "parent" : "body",
            "pivot" : [ 0, 24, 0 ]
         },
         {
            "cubes" : [
               {
                  "origin" : [ -3.9005510, 3.051758000000000e-07, -2 ],
                  "size" : [ 4, 12, 4 ],
                  "uv" : [ 0, 16 ]
               }
            ],
            "name" : "rightLeg",
            "parent" : "root",
            "pivot" : [ -1.9005510, 12, 0 ]
         },
         {
            "cubes" : [
               {
                  "inflate" : 0.25000030,
                  "origin" : [ -3.9005510, 2.670288000000000e-07, -2 ],
                  "size" : [ 4, 12, 4 ],
                  "uv" : [ 0, 32 ]
               }
            ],
            "name" : "rightPants",
            "parent" : "rightLeg",
            "pivot" : [ -1.9005510, 12, 0 ]
         },
         {
            "cubes" : [
               {
                  "origin" : [ -0.099999770, 2.288819000000000e-07, -2 ],
                  "size" : [ 4, 12, 4 ],
                  "uv" : [ 16, 48 ]
               }
            ],
            "name" : "leftLeg",
            "parent" : "root",
            "pivot" : [ 1.90, 12, 0 ]
         },
         {
            "cubes" : [
               {
                  "inflate" : 0.25000030,
                  "origin" : [ -0.099999690, 2.670288000000000e-07, -2 ],
                  "size" : [ 4, 12, 4 ],
                  "uv" : [ 0, 48 ]
               }
            ],
            "name" : "leftPants",
            "parent" : "leftLeg",
            "pivot" : [ 1.90, 12, 0 ]
         },
         { "name" : "root", "pivot" : [ 0.0, 0.0, 0.0 ] },
         { "name" : "waist", "parent" : "root", "pivot" : [ 0.0, 12.0, 0.0 ] },
         {
            "locators" : { "lead_hold" : [ -6.0, 15.0, 1.0 ] },
            "name" : "rightItem",
            "parent" : "rightArm",
            "pivot" : [ -6.0, 15.0, 1.0 ],
            "render_group_id" : 1
         },
         {
            "name" : "leftItem",
            "parent" : "leftArm",
            "pivot" : [ 6.0, 15.0, 1.0 ],
            "render_group_id" : 1
         }
      ]
   }
}

Note: No texturewidth or textureheight fields present anywhere in the geometry entry. The corresponding client_data specifies SkinImageWidth: 64 and SkinImageHeight: 64.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions