mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-17 11:24:56 -04:00
rework skin loading, support for new skin models
This fixes one of two crashes with 1.19.3
This commit is contained in:
parent
73f284650a
commit
29d1074c45
@ -21,7 +21,7 @@ import de.bixilon.minosoft.test.ITUtil
|
|||||||
import org.testng.Assert
|
import org.testng.Assert
|
||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
|
|
||||||
@Test(groups = ["pixlyzer"], dependsOnGroups = ["version"])
|
@Test(groups = ["pixlyzer"], dependsOnGroups = ["version"], singleThreaded = false, threadPoolSize = 8)
|
||||||
class PixLyzerLoadingTest {
|
class PixLyzerLoadingTest {
|
||||||
|
|
||||||
private fun Version.test() {
|
private fun Version.test() {
|
||||||
|
@ -27,7 +27,7 @@ import org.testng.Assert.assertEquals
|
|||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
|
|
||||||
|
|
||||||
@Test(groups = ["light"], dependsOnGroups = ["block"])
|
@Test(groups = ["light"], dependsOnGroups = ["block"], threadPoolSize = 8)
|
||||||
class BlockLightBreakIT {
|
class BlockLightBreakIT {
|
||||||
|
|
||||||
fun inBlock() {
|
fun inBlock() {
|
||||||
|
@ -27,7 +27,7 @@ import org.testng.Assert.assertEquals
|
|||||||
import org.testng.annotations.Test
|
import org.testng.annotations.Test
|
||||||
|
|
||||||
|
|
||||||
@Test(groups = ["light"], dependsOnGroups = ["block"])
|
@Test(groups = ["light"], dependsOnGroups = ["block"], threadPoolSize = 8)
|
||||||
class BlockLightPlaceIT {
|
class BlockLightPlaceIT {
|
||||||
|
|
||||||
fun inBlock() {
|
fun inBlock() {
|
||||||
|
@ -23,7 +23,6 @@ import de.bixilon.minosoft.util.logging.LogLevels
|
|||||||
import de.bixilon.minosoft.util.logging.LogMessageType
|
import de.bixilon.minosoft.util.logging.LogMessageType
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
open class PlayerTexture(
|
open class PlayerTexture(
|
||||||
val url: URL,
|
val url: URL,
|
||||||
@ -85,9 +84,5 @@ open class PlayerTexture(
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun UUID.isSteve(): Boolean {
|
|
||||||
return hashCode() % 2 == 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,5 +17,5 @@ import com.fasterxml.jackson.annotation.JsonInclude
|
|||||||
|
|
||||||
data class SkinMetadata(
|
data class SkinMetadata(
|
||||||
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
|
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
|
||||||
val model: SkinModel = SkinModel.NORMAL,
|
val model: SkinModel = SkinModel.WIDE,
|
||||||
)
|
)
|
||||||
|
@ -13,8 +13,11 @@
|
|||||||
|
|
||||||
package de.bixilon.minosoft.data.entities.entities.player.properties.textures.metadata
|
package de.bixilon.minosoft.data.entities.entities.player.properties.textures.metadata
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAlias
|
||||||
|
|
||||||
enum class SkinModel {
|
enum class SkinModel {
|
||||||
SLIM,
|
SLIM,
|
||||||
NORMAL,
|
@JsonAlias("normal")
|
||||||
|
WIDE,
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ class RenderWindow(
|
|||||||
val initLatch = CountUpAndDownLatch(1, latch)
|
val initLatch = CountUpAndDownLatch(1, latch)
|
||||||
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.VERBOSE) { "Generating font and gathering textures (after ${stopwatch.labTime()})..." }
|
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.VERBOSE) { "Generating font and gathering textures (after ${stopwatch.labTime()})..." }
|
||||||
textureManager.dynamicTextures.load(initLatch)
|
textureManager.dynamicTextures.load(initLatch)
|
||||||
textureManager.loadDefaultSkins(connection)
|
textureManager.initializeSkins(connection)
|
||||||
textureManager.loadDefaultTextures()
|
textureManager.loadDefaultTextures()
|
||||||
font = FontLoader.load(this, initLatch)
|
font = FontLoader.load(this, initLatch)
|
||||||
|
|
||||||
|
@ -19,23 +19,23 @@ import de.bixilon.minosoft.gui.rendering.skeletal.baked.SkeletalModelStates
|
|||||||
import de.bixilon.minosoft.gui.rendering.skeletal.instance.SkeletalInstance
|
import de.bixilon.minosoft.gui.rendering.skeletal.instance.SkeletalInstance
|
||||||
|
|
||||||
abstract class SkeletalEntityModel<E : Entity>(renderer: EntityRenderer, entity: E) : EntityModel<E>(renderer, entity) {
|
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
|
open val hideSkeletalModel: Boolean get() = false
|
||||||
|
|
||||||
|
|
||||||
override fun prepare() {
|
override fun prepare() {
|
||||||
super.prepare()
|
super.prepare()
|
||||||
if (instance.model.state != SkeletalModelStates.LOADED) {
|
if (instance?.model?.state != SkeletalModelStates.LOADED) {
|
||||||
instance.model.preload(renderWindow) // ToDo: load async
|
instance?.model?.preload(renderWindow) // ToDo: load async
|
||||||
instance.model.load()
|
instance?.model?.load()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun draw() {
|
override fun draw() {
|
||||||
super.draw()
|
super.draw()
|
||||||
if (!hideSkeletalModel) {
|
if (!hideSkeletalModel) {
|
||||||
instance.updatePosition(entity.cameraPosition, entity.rotation)
|
instance?.updatePosition(entity.cameraPosition, entity.rotation)
|
||||||
instance.draw()
|
instance?.draw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.PlayerEntity
|
||||||
import de.bixilon.minosoft.data.entities.entities.player.SkinParts
|
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.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.data.entities.entities.player.properties.textures.metadata.SkinModel
|
||||||
import de.bixilon.minosoft.gui.rendering.entity.EntityRenderer
|
import de.bixilon.minosoft.gui.rendering.entity.EntityRenderer
|
||||||
import de.bixilon.minosoft.gui.rendering.entity.models.SkeletalEntityModel
|
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.DynamicStateChangeCallback
|
||||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.dynamic.DynamicTexture
|
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.dynamic.DynamicTextureState
|
||||||
|
import de.bixilon.minosoft.gui.rendering.system.base.texture.skin.PlayerSkin
|
||||||
import de.bixilon.minosoft.util.KUtil.toResourceLocation
|
import de.bixilon.minosoft.util.KUtil.toResourceLocation
|
||||||
|
|
||||||
open class PlayerModel(renderer: EntityRenderer, player: PlayerEntity) : SkeletalEntityModel<PlayerEntity>(renderer, player), DynamicStateChangeCallback {
|
open class PlayerModel(renderer: EntityRenderer, player: PlayerEntity) : SkeletalEntityModel<PlayerEntity>(renderer, player), DynamicStateChangeCallback {
|
||||||
private var properties = player.additional.properties
|
private var properties = player.additional.properties
|
||||||
private var skin: DynamicTexture? = null
|
private var skin: PlayerSkin? = null
|
||||||
protected var refreshModel = false
|
protected var refreshModel = false
|
||||||
|
|
||||||
private val legAnimator = LegAnimator(this)
|
private val legAnimator = LegAnimator(this)
|
||||||
@ -52,8 +52,9 @@ open class PlayerModel(renderer: EntityRenderer, player: PlayerEntity) : Skeleta
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun createModel(properties: PlayerProperties?): SkeletalInstance {
|
private fun createModel(properties: PlayerProperties?): SkeletalInstance? {
|
||||||
val skinModel = properties?.textures?.skin?.metadata?.model ?: if (entity.uuid?.isSteve() == true) SkinModel.NORMAL else SkinModel.SLIM
|
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 unbaked = renderWindow.modelLoader.entities.loadUnbakedModel(if (skinModel == SkinModel.SLIM) SLIM_MODEL else NORMAL_MODEL)
|
||||||
|
|
||||||
val elements: MutableList<SkeletalElement> = mutableListOf()
|
val elements: MutableList<SkeletalElement> = mutableListOf()
|
||||||
@ -66,15 +67,14 @@ open class PlayerModel(renderer: EntityRenderer, player: PlayerEntity) : Skeleta
|
|||||||
}
|
}
|
||||||
elements += element
|
elements += element
|
||||||
}
|
}
|
||||||
val skin = renderWindow.textureManager.getSkin(entity, properties)
|
skin.texture.usages.incrementAndGet()
|
||||||
skin.usages.incrementAndGet()
|
this.skin?.texture?.usages?.decrementAndGet()
|
||||||
this.skin?.usages?.decrementAndGet()
|
|
||||||
this.skin = skin
|
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)
|
val instance = SkeletalInstance(renderWindow, model)
|
||||||
|
|
||||||
@ -110,13 +110,13 @@ open class PlayerModel(renderer: EntityRenderer, player: PlayerEntity) : Skeleta
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onStateChange(texture: DynamicTexture, state: DynamicTextureState) {
|
override fun onStateChange(texture: DynamicTexture, state: DynamicTextureState) {
|
||||||
if (skin === texture) {
|
if (skin?.texture === texture) {
|
||||||
refreshModel = true
|
refreshModel = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun unload() {
|
override fun unload() {
|
||||||
skin?.usages?.decrementAndGet()
|
skin?.texture?.usages?.decrementAndGet()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun swingArm(arm: Arms) {
|
fun swingArm(arm: Arms) {
|
||||||
|
@ -81,7 +81,7 @@ class TabListEntryElement(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
background = ColorElement(guiRenderer, size, RGBColor(120, 120, 120, 130))
|
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()
|
forceSilentApply()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,27 +15,16 @@ package de.bixilon.minosoft.gui.rendering.system.base.texture
|
|||||||
|
|
||||||
import de.bixilon.kotlinglm.vec2.Vec2
|
import de.bixilon.kotlinglm.vec2.Vec2
|
||||||
import de.bixilon.kotlinglm.vec2.Vec2i
|
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.RenderConstants
|
||||||
import de.bixilon.minosoft.gui.rendering.gui.atlas.TextureLikeTexture
|
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.NativeShader
|
||||||
import de.bixilon.minosoft.gui.rendering.system.base.shader.ShaderUniforms
|
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.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.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.gui.rendering.textures.TextureUtil.texture
|
||||||
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
||||||
import de.bixilon.minosoft.util.KUtil.toResourceLocation
|
import de.bixilon.minosoft.util.KUtil.minosoft
|
||||||
import de.bixilon.minosoft.util.logging.Log
|
|
||||||
import de.bixilon.minosoft.util.logging.LogLevels
|
|
||||||
import de.bixilon.minosoft.util.logging.LogMessageType
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
abstract class TextureManager {
|
abstract class TextureManager {
|
||||||
abstract val staticTextures: StaticTextureArray
|
abstract val staticTextures: StaticTextureArray
|
||||||
@ -45,11 +34,7 @@ abstract class TextureManager {
|
|||||||
private set
|
private set
|
||||||
lateinit var whiteTexture: TextureLikeTexture
|
lateinit var whiteTexture: TextureLikeTexture
|
||||||
private set
|
private set
|
||||||
lateinit var steveTexture: DynamicTexture
|
lateinit var skins: SkinManager
|
||||||
private set
|
|
||||||
lateinit var alexTexture: DynamicTexture
|
|
||||||
private set
|
|
||||||
lateinit var skin: DynamicTexture
|
|
||||||
private set
|
private set
|
||||||
|
|
||||||
fun loadDefaultTextures() {
|
fun loadDefaultTextures() {
|
||||||
@ -57,52 +42,12 @@ abstract class TextureManager {
|
|||||||
throw IllegalStateException("Already initialized!")
|
throw IllegalStateException("Already initialized!")
|
||||||
}
|
}
|
||||||
debugTexture = staticTextures.createTexture(RenderConstants.DEBUG_TEXTURE_RESOURCE_LOCATION)
|
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) {
|
fun initializeSkins(connection: PlayConnection) {
|
||||||
steveTexture = dynamicTextures.pushBuffer(UUID(0L, 0L), true) { connection.assetsManager["minecraft:entity/steve".toResourceLocation().texture()].readTexture() }.apply { usages.incrementAndGet() }
|
skins = SkinManager(this)
|
||||||
alexTexture = dynamicTextures.pushBuffer(UUID(1L, 0L), true) { connection.assetsManager["minecraft:entity/alex".toResourceLocation().texture()].readTexture() }.apply { usages.incrementAndGet() }
|
skins.initialize(connection.account, connection.assetsManager)
|
||||||
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 use(shader: NativeShader, name: String = ShaderUniforms.TEXTURES) {
|
fun use(shader: NativeShader, name: String = ShaderUniforms.TEXTURES) {
|
||||||
|
@ -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,
|
||||||
|
)
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
@ -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,
|
||||||
|
)
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user