rework skin loading, support for new skin models

This fixes one of two crashes with 1.19.3
This commit is contained in:
Bixilon 2022-12-08 16:26:52 +01:00
parent 73f284650a
commit 29d1074c45
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
17 changed files with 335 additions and 92 deletions

View File

@ -21,7 +21,7 @@ import de.bixilon.minosoft.test.ITUtil
import org.testng.Assert
import org.testng.annotations.Test
@Test(groups = ["pixlyzer"], dependsOnGroups = ["version"])
@Test(groups = ["pixlyzer"], dependsOnGroups = ["version"], singleThreaded = false, threadPoolSize = 8)
class PixLyzerLoadingTest {
private fun Version.test() {

View File

@ -27,7 +27,7 @@ import org.testng.Assert.assertEquals
import org.testng.annotations.Test
@Test(groups = ["light"], dependsOnGroups = ["block"])
@Test(groups = ["light"], dependsOnGroups = ["block"], threadPoolSize = 8)
class BlockLightBreakIT {
fun inBlock() {

View File

@ -27,7 +27,7 @@ import org.testng.Assert.assertEquals
import org.testng.annotations.Test
@Test(groups = ["light"], dependsOnGroups = ["block"])
@Test(groups = ["light"], dependsOnGroups = ["block"], threadPoolSize = 8)
class BlockLightPlaceIT {
fun inBlock() {

View File

@ -23,7 +23,6 @@ import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
import java.io.File
import java.net.URL
import java.util.*
open class PlayerTexture(
val url: URL,
@ -85,9 +84,5 @@ open class PlayerTexture(
}
return false
}
fun UUID.isSteve(): Boolean {
return hashCode() % 2 == 0
}
}
}

View File

@ -17,5 +17,5 @@ import com.fasterxml.jackson.annotation.JsonInclude
data class SkinMetadata(
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
val model: SkinModel = SkinModel.NORMAL,
val model: SkinModel = SkinModel.WIDE,
)

View File

@ -13,8 +13,11 @@
package de.bixilon.minosoft.data.entities.entities.player.properties.textures.metadata
import com.fasterxml.jackson.annotation.JsonAlias
enum class SkinModel {
SLIM,
NORMAL,
@JsonAlias("normal")
WIDE,
;
}

View File

@ -159,7 +159,7 @@ class RenderWindow(
val initLatch = CountUpAndDownLatch(1, latch)
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.VERBOSE) { "Generating font and gathering textures (after ${stopwatch.labTime()})..." }
textureManager.dynamicTextures.load(initLatch)
textureManager.loadDefaultSkins(connection)
textureManager.initializeSkins(connection)
textureManager.loadDefaultTextures()
font = FontLoader.load(this, initLatch)

View File

@ -19,23 +19,23 @@ import de.bixilon.minosoft.gui.rendering.skeletal.baked.SkeletalModelStates
import de.bixilon.minosoft.gui.rendering.skeletal.instance.SkeletalInstance
abstract class SkeletalEntityModel<E : Entity>(renderer: EntityRenderer, entity: E) : EntityModel<E>(renderer, entity) {
abstract val instance: SkeletalInstance
abstract val instance: SkeletalInstance?
open val hideSkeletalModel: Boolean get() = false
override fun prepare() {
super.prepare()
if (instance.model.state != SkeletalModelStates.LOADED) {
instance.model.preload(renderWindow) // ToDo: load async
instance.model.load()
if (instance?.model?.state != SkeletalModelStates.LOADED) {
instance?.model?.preload(renderWindow) // ToDo: load async
instance?.model?.load()
}
}
override fun draw() {
super.draw()
if (!hideSkeletalModel) {
instance.updatePosition(entity.cameraPosition, entity.rotation)
instance.draw()
instance?.updatePosition(entity.cameraPosition, entity.rotation)
instance?.draw()
}
}
}

View File

@ -19,7 +19,6 @@ import de.bixilon.minosoft.data.entities.entities.player.Arms
import de.bixilon.minosoft.data.entities.entities.player.PlayerEntity
import de.bixilon.minosoft.data.entities.entities.player.SkinParts
import de.bixilon.minosoft.data.entities.entities.player.properties.PlayerProperties
import de.bixilon.minosoft.data.entities.entities.player.properties.textures.PlayerTexture.Companion.isSteve
import de.bixilon.minosoft.data.entities.entities.player.properties.textures.metadata.SkinModel
import de.bixilon.minosoft.gui.rendering.entity.EntityRenderer
import de.bixilon.minosoft.gui.rendering.entity.models.SkeletalEntityModel
@ -30,11 +29,12 @@ import de.bixilon.minosoft.gui.rendering.skeletal.model.elements.SkeletalElement
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicStateChangeCallback
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureState
import de.bixilon.minosoft.gui.rendering.system.base.texture.skin.PlayerSkin
import de.bixilon.minosoft.util.KUtil.toResourceLocation
open class PlayerModel(renderer: EntityRenderer, player: PlayerEntity) : SkeletalEntityModel<PlayerEntity>(renderer, player), DynamicStateChangeCallback {
private var properties = player.additional.properties
private var skin: DynamicTexture? = null
private var skin: PlayerSkin? = null
protected var refreshModel = false
private val legAnimator = LegAnimator(this)
@ -52,8 +52,9 @@ open class PlayerModel(renderer: EntityRenderer, player: PlayerEntity) : Skeleta
}
private fun createModel(properties: PlayerProperties?): SkeletalInstance {
val skinModel = properties?.textures?.skin?.metadata?.model ?: if (entity.uuid?.isSteve() == true) SkinModel.NORMAL else SkinModel.SLIM
private fun createModel(properties: PlayerProperties?): SkeletalInstance? {
val skin = renderWindow.textureManager.skins.getSkin(entity, properties) ?: return null
val skinModel = skin.model
val unbaked = renderWindow.modelLoader.entities.loadUnbakedModel(if (skinModel == SkinModel.SLIM) SLIM_MODEL else NORMAL_MODEL)
val elements: MutableList<SkeletalElement> = mutableListOf()
@ -66,15 +67,14 @@ open class PlayerModel(renderer: EntityRenderer, player: PlayerEntity) : Skeleta
}
elements += element
}
val skin = renderWindow.textureManager.getSkin(entity, properties)
skin.usages.incrementAndGet()
this.skin?.usages?.decrementAndGet()
skin.texture.usages.incrementAndGet()
this.skin?.texture?.usages?.decrementAndGet()
this.skin = skin
skin += this
skin.texture += this
val skinTexture = if (skin.state != DynamicTextureState.LOADED) renderWindow.textureManager.getFallbackTexture(entity.uuid) else skin
val skinTexture = if (skin.texture.state != DynamicTextureState.LOADED) renderWindow.textureManager.skins.default[entity.uuid] ?: return null else skin
val model = unbaked.copy(elements = elements, animations = animations).bake(renderWindow, mapOf(0 to skinTexture))
val model = unbaked.copy(elements = elements, animations = animations).bake(renderWindow, mapOf(0 to skinTexture.texture))
val instance = SkeletalInstance(renderWindow, model)
@ -110,13 +110,13 @@ open class PlayerModel(renderer: EntityRenderer, player: PlayerEntity) : Skeleta
}
override fun onStateChange(texture: DynamicTexture, state: DynamicTextureState) {
if (skin === texture) {
if (skin?.texture === texture) {
refreshModel = true
}
}
override fun unload() {
skin?.usages?.decrementAndGet()
skin?.texture?.usages?.decrementAndGet()
}
fun swingArm(arm: Arms) {

View File

@ -81,7 +81,7 @@ class TabListEntryElement(
init {
background = ColorElement(guiRenderer, size, RGBColor(120, 120, 120, 130))
DefaultThreadPool += { skinElement.texture = renderWindow.textureManager.getSkin(guiRenderer.connection.network.encrypted, uuid, item.properties) }
DefaultThreadPool += { skinElement.texture = renderWindow.textureManager.skins.getSkin(uuid, item.properties, fetch = guiRenderer.connection.network.encrypted)?.texture }
forceSilentApply()
}

View File

@ -15,27 +15,16 @@ package de.bixilon.minosoft.gui.rendering.system.base.texture
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.config.profile.profiles.account.AccountProfileManager
import de.bixilon.minosoft.data.entities.entities.player.PlayerEntity
import de.bixilon.minosoft.data.entities.entities.player.local.LocalPlayerEntity
import de.bixilon.minosoft.data.entities.entities.player.properties.PlayerProperties
import de.bixilon.minosoft.data.entities.entities.player.properties.textures.PlayerTexture.Companion.isSteve
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.gui.atlas.TextureLikeTexture
import de.bixilon.minosoft.gui.rendering.system.base.shader.NativeShader
import de.bixilon.minosoft.gui.rendering.system.base.shader.ShaderUniforms
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureArray
import de.bixilon.minosoft.gui.rendering.system.base.texture.skin.SkinManager
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.readTexture
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
import java.util.*
import de.bixilon.minosoft.util.KUtil.minosoft
abstract class TextureManager {
abstract val staticTextures: StaticTextureArray
@ -45,11 +34,7 @@ abstract class TextureManager {
private set
lateinit var whiteTexture: TextureLikeTexture
private set
lateinit var steveTexture: DynamicTexture
private set
lateinit var alexTexture: DynamicTexture
private set
lateinit var skin: DynamicTexture
lateinit var skins: SkinManager
private set
fun loadDefaultTextures() {
@ -57,52 +42,12 @@ abstract class TextureManager {
throw IllegalStateException("Already initialized!")
}
debugTexture = staticTextures.createTexture(RenderConstants.DEBUG_TEXTURE_RESOURCE_LOCATION)
whiteTexture = TextureLikeTexture(texture = staticTextures.createTexture(ResourceLocation("minosoft:textures/white.png")), uvStart = Vec2(0.0f, 0.0f), uvEnd = Vec2(0.001f, 0.001f), size = Vec2i(16, 16))
whiteTexture = TextureLikeTexture(texture = staticTextures.createTexture(minosoft("white").texture()), uvStart = Vec2(0.0f, 0.0f), uvEnd = Vec2(0.001f, 0.001f), size = Vec2i(16, 16))
}
fun loadDefaultSkins(connection: PlayConnection) {
steveTexture = dynamicTextures.pushBuffer(UUID(0L, 0L), true) { connection.assetsManager["minecraft:entity/steve".toResourceLocation().texture()].readTexture() }.apply { usages.incrementAndGet() }
alexTexture = dynamicTextures.pushBuffer(UUID(1L, 0L), true) { connection.assetsManager["minecraft:entity/alex".toResourceLocation().texture()].readTexture() }.apply { usages.incrementAndGet() }
skin = getSkin(connection.account.supportsSkins, connection.account.uuid, connection.account.properties, force = true).apply { usages.incrementAndGet() }
}
fun getSkin(fetchSkin: Boolean, uuid: UUID, properties: PlayerProperties?, force: Boolean = false): DynamicTexture {
var properties = properties
if (properties?.textures == null) {
for (account in AccountProfileManager.selected.entries.values) {
if (account.uuid == uuid) {
properties = account.properties
}
}
if (properties?.textures == null && fetchSkin) {
try {
properties = PlayerProperties.fetch(uuid)
} catch (ignored: Throwable) {
}
}
}
properties?.textures?.skin?.let { return dynamicTextures.pushRawArray(uuid, force) { it.read() } }
return getFallbackTexture(uuid)
}
fun getSkin(player: PlayerEntity, properties: PlayerProperties? = player.additional.properties): DynamicTexture {
if (player is LocalPlayerEntity) {
return skin
}
val uuid = player.uuid
if (uuid == null) {
Log.log(LogMessageType.OTHER, LogLevels.VERBOSE) { "Player uuid is null: $player" }
return steveTexture
}
return getSkin(true, uuid, properties)
}
fun getFallbackTexture(uuid: UUID?): DynamicTexture {
if (uuid == null || uuid.isSteve()) {
return steveTexture
}
return alexTexture
fun initializeSkins(connection: PlayConnection) {
skins = SkinManager(this)
skins.initialize(connection.account, connection.assetsManager)
}
fun use(shader: NativeShader, name: String = ShaderUniforms.TEXTURES) {

View File

@ -0,0 +1,22 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.system.base.texture.skin
import de.bixilon.minosoft.data.entities.entities.player.properties.textures.metadata.SkinModel
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture
class PlayerSkin(
val texture: DynamicTexture,
val model: SkinModel,
)

View File

@ -0,0 +1,69 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.system.base.texture.skin
import de.bixilon.kutil.exception.ExceptionUtil.catchAll
import de.bixilon.minosoft.assets.AssetsManager
import de.bixilon.minosoft.config.profile.profiles.account.AccountProfileManager
import de.bixilon.minosoft.data.accounts.Account
import de.bixilon.minosoft.data.entities.entities.player.PlayerEntity
import de.bixilon.minosoft.data.entities.entities.player.local.LocalPlayerEntity
import de.bixilon.minosoft.data.entities.entities.player.properties.PlayerProperties
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureManager
import de.bixilon.minosoft.gui.rendering.system.base.texture.skin.vanilla.DefaultSkinProvider
import java.util.*
class SkinManager(private val textureManager: TextureManager) {
lateinit var default: DefaultSkinProvider
private set
private var skin: PlayerSkin? = null
fun initialize(account: Account, assets: AssetsManager) {
default = DefaultSkinProvider(this.textureManager.dynamicTextures, assets)
skin = getSkin(account.uuid, account.properties, fetch = true, force = true)?.apply { texture.usages.incrementAndGet() }
}
private fun getAccountProperties(uuid: UUID): PlayerProperties? {
for (account in AccountProfileManager.selected.entries.values) {
if (account.uuid != uuid) {
continue
}
return account.properties
}
return null
}
private fun getProperties(player: PlayerEntity, uuid: UUID, fetch: Boolean): PlayerProperties? {
return player.additional.properties ?: getAccountProperties(uuid) ?: if (fetch) catchAll { PlayerProperties.fetch(uuid) } else null
}
private fun getSkin(uuid: UUID, properties: PlayerProperties?, force: Boolean = false): PlayerSkin? {
val texture = properties?.textures?.skin ?: return default[uuid]
return PlayerSkin(textureManager.dynamicTextures.pushRawArray(uuid, force) { texture.read() }, texture.metadata.model)
}
fun getSkin(player: PlayerEntity, properties: PlayerProperties? = null, fetch: Boolean = true, force: Boolean = false): PlayerSkin? {
if (player is LocalPlayerEntity) {
return skin
}
val uuid = player.uuid ?: return default[player]
return getSkin(uuid, properties ?: getProperties(player, uuid, fetch), force)
}
fun getSkin(uuid: UUID?, properties: PlayerProperties? = null, fetch: Boolean = true, force: Boolean = false): PlayerSkin? {
if (uuid == null) return default[null]
return getSkin(uuid, properties ?: if (fetch) catchAll { PlayerProperties.fetch(uuid) } else null, force)
}
}

View File

@ -0,0 +1,23 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.system.base.texture.skin.vanilla
import de.bixilon.minosoft.data.entities.entities.player.properties.textures.metadata.SkinModel
import de.bixilon.minosoft.data.registries.ResourceLocation
open class DefaultLegacySkin(
name: ResourceLocation,
val model: SkinModel,
val fallback: Boolean,
) : DefaultSkin(name)

View File

@ -0,0 +1,20 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.system.base.texture.skin.vanilla
import de.bixilon.minosoft.data.registries.ResourceLocation
open class DefaultSkin(
val name: ResourceLocation,
)

View File

@ -0,0 +1,124 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.system.base.texture.skin.vanilla
import de.bixilon.kotlinglm.GLM.abs
import de.bixilon.minosoft.assets.AssetsManager
import de.bixilon.minosoft.data.entities.entities.player.PlayerEntity
import de.bixilon.minosoft.data.entities.entities.player.properties.textures.metadata.SkinModel
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTextureArray
import de.bixilon.minosoft.gui.rendering.system.base.texture.skin.PlayerSkin
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.readTexture
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture
import java.util.*
class DefaultSkinProvider(
private val array: DynamicTextureArray,
private val assets: AssetsManager,
) {
private var defaultId = 0
private val slim: MutableMap<ResourceLocation, DynamicTexture> = mutableMapOf()
private val wide: MutableMap<ResourceLocation, DynamicTexture> = mutableMapOf()
private var fallback: PlayerSkin? = null
fun initialize() {
for (skin in DefaultSkins) {
load(skin)
}
}
private fun load(skin: DefaultSkin) {
var loaded = 0
load(skin.name.skin("slim").texture())?.let { slim[skin.name] = it; loaded++ }
load(skin.name.skin("wide").texture())?.let { wide[skin.name] = it; loaded++ }
if (loaded > 0) {
return
}
if (skin is DefaultLegacySkin) {
loadLegacy(skin)
}
}
private fun loadLegacy(skin: DefaultLegacySkin) {
val path = ResourceLocation(skin.name.namespace, "entity/${skin.name.path}").texture()
val texture = load(path) ?: return
this[skin.model][skin.name] = texture
if (skin.fallback) {
this.fallback = PlayerSkin(texture, skin.model)
}
}
private fun load(path: ResourceLocation): DynamicTexture? {
val data = assets.getOrNull(path)?.readTexture() ?: return null
val texture = array.pushBuffer(UUID(0L, defaultId++.toLong()), true) { data }
texture.usages.incrementAndGet()
return texture
}
private fun ResourceLocation.skin(prefix: String): ResourceLocation {
return ResourceLocation(namespace, "entity/player/$prefix/$path")
}
operator fun get(name: ResourceLocation, slim: Boolean): DynamicTexture? {
val map = if (slim) this.slim else this.wide
return map[name]
}
private operator fun get(model: SkinModel): MutableMap<ResourceLocation, DynamicTexture> {
return when (model) {
SkinModel.SLIM -> this.slim
SkinModel.WIDE -> this.wide
}
}
private fun UUID.isSteve(): Boolean {
return hashCode() % 2 == 0
}
operator fun get(skin: DefaultSkin, model: SkinModel): PlayerSkin? {
return this[model][skin.name]?.let { PlayerSkin(it, model) }
}
operator fun get(uuid: UUID?): PlayerSkin? {
if (uuid == null) {
return fallback
}
if (this.slim.size <= 1) {
return getLegacy(uuid) ?: fallback
}
// TODO: verify with vanilla
val count = DefaultSkins.SKINS.size
val hash = abs(uuid.hashCode()) % count
val model = if (hash > count / 2) SkinModel.WIDE else SkinModel.SLIM
return this[DefaultSkins.SKINS[hash / 2], model] ?: getLegacy(uuid)
}
fun getLegacy(uuid: UUID): PlayerSkin? {
val skin = if (uuid.isSteve()) DefaultSkins.STEVE else DefaultSkins.ALEX
if (skin.fallback) {
return this.fallback
}
return this[skin, skin.model]
}
operator fun get(player: PlayerEntity): PlayerSkin? {
return this[player.uuid] ?: return fallback
}
}

View File

@ -0,0 +1,42 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.system.base.texture.skin.vanilla
import de.bixilon.minosoft.data.entities.entities.player.properties.textures.metadata.SkinModel
import de.bixilon.minosoft.util.KUtil.minecraft
object DefaultSkins : Iterable<DefaultSkin> {
val SKINS: MutableList<DefaultSkin> = mutableListOf()
val ALEX = DefaultLegacySkin(minecraft("alex"), SkinModel.SLIM, fallback = false).register()
val ARI = DefaultSkin(minecraft("ari")).register()
val EFE = DefaultSkin(minecraft("efe")).register()
val KAI = DefaultSkin(minecraft("kai")).register()
val MAKENA = DefaultSkin(minecraft("makena")).register()
val NOOR = DefaultSkin(minecraft("noor")).register()
val STEVE = DefaultLegacySkin(minecraft("steve"), SkinModel.WIDE, fallback = true).register()
val SUNNY = DefaultSkin(minecraft("sunny")).register()
val ZURI = DefaultSkin(minecraft("zuri")).register()
private fun <T : DefaultSkin> T.register(): T {
SKINS += this
return this
}
override fun iterator(): Iterator<DefaultSkin> {
return SKINS.iterator()
}
}