diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/data/buffer/RGBA8Buffer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/data/buffer/RGBA8Buffer.kt index 34a4b369d..98f861503 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/data/buffer/RGBA8Buffer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/data/buffer/RGBA8Buffer.kt @@ -58,6 +58,9 @@ class RGBA8Buffer( override fun getRGBA(x: Int, y: Int): Int { val stride = stride(x, y) + if (stride > data.capacity()) { + throw IllegalArgumentException("Can not access pixel at ($x,$y), exceeds size: $size") + } return (this[stride + 0] shl 24) or (this[stride + 1] shl 16) or (this[stride + 2] shl 8) or (this[stride + 3] shl 0) } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicTextureArray.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicTextureArray.kt index b69aabc5a..ab73d1892 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicTextureArray.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicTextureArray.kt @@ -24,6 +24,9 @@ import de.bixilon.minosoft.gui.rendering.system.base.texture.array.TextureArray import de.bixilon.minosoft.gui.rendering.system.base.texture.data.MipmapTextureData import de.bixilon.minosoft.gui.rendering.system.base.texture.data.buffer.TextureBuffer import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.readTexture +import de.bixilon.minosoft.util.logging.Log +import de.bixilon.minosoft.util.logging.LogLevels +import de.bixilon.minosoft.util.logging.LogMessageType import java.io.ByteArrayInputStream import java.lang.ref.WeakReference @@ -70,7 +73,14 @@ abstract class DynamicTextureArray( } private fun DynamicTexture.load(index: Int, creator: () -> TextureBuffer) { - val buffer = creator.invoke() + val buffer = try { + creator.invoke() + } catch (error: Throwable) { + Log.log(LogMessageType.RENDERING, LogLevels.WARN) { "Could not load dynamic texture (index=$index, identifier=${this.identifier}): $error" } + error.printStackTrace() + this.state = DynamicTextureState.ERROR + return + } this.data = MipmapTextureData(buffer, mipmaps) if (Thread.currentThread() == context.thread) { @@ -147,7 +157,7 @@ abstract class DynamicTextureArray( for ((index, reference) in textures.withIndex()) { if (reference == null) continue val texture = reference.get() - if (texture != null) continue // not gced yet, keep it for now + if (texture != null && texture.state != DynamicTextureState.ERROR) continue // not gced yet, keep it for now textures[index] = null } lock.unlock() diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicTextureState.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicTextureState.kt index 2963e2e00..ffcc50c61 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicTextureState.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/dynamic/DynamicTextureState.kt @@ -17,6 +17,7 @@ enum class DynamicTextureState { WAITING, LOADING, LOADED, + ERROR, @Deprecated("unrechable", level = DeprecationLevel.ERROR) UNLOADED, diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/skin/IllegalSkinError.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/skin/IllegalSkinError.kt new file mode 100644 index 000000000..9f5c5c825 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/skin/IllegalSkinError.kt @@ -0,0 +1,16 @@ +/* + * Minosoft + * Copyright (C) 2020-2023 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.gui.rendering.system.base.texture.skin + +class IllegalSkinError(message: String? = null) : Exception(message) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/skin/SkinManager.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/skin/SkinManager.kt index 3925bcc56..f2af11916 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/skin/SkinManager.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/skin/SkinManager.kt @@ -30,13 +30,13 @@ import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY_INSTANCE import java.io.ByteArrayInputStream import java.util.* -class SkinManager(private val textureManager: TextureManager) { +class SkinManager(private val textures: TextureManager) { lateinit var default: DefaultSkinProvider private set private var skin: PlayerSkin? = null fun initialize(account: Account, assets: AssetsManager) { - default = DefaultSkinProvider(this.textureManager.dynamic, assets) + default = DefaultSkinProvider(this.textures.dynamic, assets) default.initialize() skin = getSkin(account.uuid, account.properties, fetch = true, async = false) } @@ -57,7 +57,7 @@ class SkinManager(private val textureManager: TextureManager) { private fun getSkin(uuid: UUID, properties: PlayerProperties?, async: Boolean = true): PlayerSkin? { val texture = properties?.textures?.skin ?: return default[uuid] - return PlayerSkin(textureManager.dynamic.push(texture.getHash(), async) { texture.read().readSkin() }, default[uuid]?.texture, texture.metadata.model) + return PlayerSkin(textures.dynamic.push(texture.getHash(), async) { texture.read().readSkin() }, default[uuid]?.texture, texture.metadata.model) } fun getSkin(player: PlayerEntity, properties: PlayerProperties? = null, fetch: Boolean = true, async: Boolean = true): PlayerSkin? { @@ -76,16 +76,22 @@ class SkinManager(private val textureManager: TextureManager) { private fun ByteArray.readSkin(): TextureBuffer { val data = ByteArrayInputStream(this).readTexture() - if (data.size.y != 32) return data + if (data.size.x != 64) throw IllegalSkinError("Width of skin must be 64 pixels: ${data.size}") + return when (data.size.y) { + 32 -> { + // <1.8 legacy skin + val next = RGBA8Buffer(Vec2i(64)) + next.put(data, Vec2i.EMPTY_INSTANCE, Vec2i.EMPTY_INSTANCE, data.size) - val next = RGBA8Buffer(Vec2i(64)) - next.put(data, Vec2i.EMPTY_INSTANCE, Vec2i.EMPTY_INSTANCE, data.size) + next.put(next, Vec2i(0, 16), Vec2i(16, 48), Vec2i(16, 16))// leg [0, 16][16,16] to left leg [16, 48] + next.put(next, Vec2i(40, 16), Vec2i(32, 48), Vec2i(16, 16)) // arm [40, 16] to left arm [32, 48] - next.put(next, Vec2i(0, 16), Vec2i(16, 48), Vec2i(16, 16))// leg [0, 16][16,16] to left leg [16, 48] - next.put(next, Vec2i(40, 16), Vec2i(32, 48), Vec2i(16, 16)) // arm [40, 16] to left arm [32, 48] + // TODO: flip every texture part + return data + } - // TODO: flip every texture part - - return next + 64 -> data // skin + else -> throw IllegalSkinError("Can not detect skin format: ${data.size}") + } } }