verify skin size

This commit is contained in:
Moritz Zwerger 2023-12-05 23:37:49 +01:00
parent 803cdcc552
commit acb3bd3b02
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
5 changed files with 49 additions and 13 deletions

View File

@ -58,6 +58,9 @@ class RGBA8Buffer(
override fun getRGBA(x: Int, y: Int): Int { override fun getRGBA(x: Int, y: Int): Int {
val stride = stride(x, y) 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) return (this[stride + 0] shl 24) or (this[stride + 1] shl 16) or (this[stride + 2] shl 8) or (this[stride + 3] shl 0)
} }

View File

@ -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.MipmapTextureData
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.buffer.TextureBuffer 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.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.io.ByteArrayInputStream
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
@ -70,7 +73,14 @@ abstract class DynamicTextureArray(
} }
private fun DynamicTexture.load(index: Int, creator: () -> TextureBuffer) { 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) this.data = MipmapTextureData(buffer, mipmaps)
if (Thread.currentThread() == context.thread) { if (Thread.currentThread() == context.thread) {
@ -147,7 +157,7 @@ abstract class DynamicTextureArray(
for ((index, reference) in textures.withIndex()) { for ((index, reference) in textures.withIndex()) {
if (reference == null) continue if (reference == null) continue
val texture = reference.get() 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 textures[index] = null
} }
lock.unlock() lock.unlock()

View File

@ -17,6 +17,7 @@ enum class DynamicTextureState {
WAITING, WAITING,
LOADING, LOADING,
LOADED, LOADED,
ERROR,
@Deprecated("unrechable", level = DeprecationLevel.ERROR) @Deprecated("unrechable", level = DeprecationLevel.ERROR)
UNLOADED, UNLOADED,

View File

@ -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 <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
class IllegalSkinError(message: String? = null) : Exception(message)

View File

@ -30,13 +30,13 @@ import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY_INSTANCE
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.util.* import java.util.*
class SkinManager(private val textureManager: TextureManager) { class SkinManager(private val textures: TextureManager) {
lateinit var default: DefaultSkinProvider lateinit var default: DefaultSkinProvider
private set private set
private var skin: PlayerSkin? = null private var skin: PlayerSkin? = null
fun initialize(account: Account, assets: AssetsManager) { fun initialize(account: Account, assets: AssetsManager) {
default = DefaultSkinProvider(this.textureManager.dynamic, assets) default = DefaultSkinProvider(this.textures.dynamic, assets)
default.initialize() default.initialize()
skin = getSkin(account.uuid, account.properties, fetch = true, async = false) 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? { private fun getSkin(uuid: UUID, properties: PlayerProperties?, async: Boolean = true): PlayerSkin? {
val texture = properties?.textures?.skin ?: return default[uuid] 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? { fun getSkin(player: PlayerEntity, properties: PlayerProperties? = null, fetch: Boolean = true, async: Boolean = true): PlayerSkin? {
@ -76,8 +76,10 @@ class SkinManager(private val textureManager: TextureManager) {
private fun ByteArray.readSkin(): TextureBuffer { private fun ByteArray.readSkin(): TextureBuffer {
val data = ByteArrayInputStream(this).readTexture() 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)) val next = RGBA8Buffer(Vec2i(64))
next.put(data, Vec2i.EMPTY_INSTANCE, Vec2i.EMPTY_INSTANCE, data.size) next.put(data, Vec2i.EMPTY_INSTANCE, Vec2i.EMPTY_INSTANCE, data.size)
@ -85,7 +87,11 @@ class SkinManager(private val textureManager: TextureManager) {
next.put(next, Vec2i(40, 16), Vec2i(32, 48), Vec2i(16, 16)) // arm [40, 16] to left arm [32, 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 // TODO: flip every texture part
return data
}
return next 64 -> data // skin
else -> throw IllegalSkinError("Can not detect skin format: ${data.size}")
}
} }
} }