As for the entity customizations, it’s still very much WIP, but I’ve torn apart the basics in another post of mine.
What it does, currently, is building a tree, starting with the “root” variant and then go all the way down until it reaches a variant that has no child-variants. In simplified pseudo-lua, it would be
function check_variant(variant)
if variant == nil then return end
if variant.models then add_model(variant.models[1]) end
if variant.variants then check_variant(get_random_variant(variant.variants)) end
end
check_variant("root")
In theory, I suppose, you could even have loops, but I strictly advise against that… It’s not that difficult if you visualise it perhaps.
root is the first variant to be loaded, that’s pretty much hardcoded. After that, it is taking a random sub-variant until there’s no sub variant. In this example, circles are just variants for ordering’s sake, whereas rectangles set a model. An arrow indicates a sub-variant, i.e. root has only sub variant, young, which will always be taken, whereas young has three sub-variants and young_blonde, young_brown and young_sandy are all taken at approximately 33% chance each.
If a variant adds a model, it’s really just thrown onto the default model variant - which, unless your mod does something else, is the one always used. There are only a few cases where a different model variant is used by SH, most prominently the berry bush. Villagers, so far, use only the default one.