diff --git a/pom.xml b/pom.xml
index 8d44dbc4e..f7a62946e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -444,5 +444,10 @@
reflections
0.10.2
+
+ com.github.luben
+ zstd-jni
+ 1.5.0-4
+
diff --git a/src/main/java/de/bixilon/minosoft/Minosoft.kt b/src/main/java/de/bixilon/minosoft/Minosoft.kt
index f960fce3f..e1f5257d6 100644
--- a/src/main/java/de/bixilon/minosoft/Minosoft.kt
+++ b/src/main/java/de/bixilon/minosoft/Minosoft.kt
@@ -122,6 +122,8 @@ object Minosoft {
Util.forceClassInit(Eros::class.java)
}
+ taskWorker += Task(identifier = StartupTasks.LOAD_YGGDRASIL, executor = { YggdrasilUtil.load() })
+
taskWorker.work(START_UP_LATCH)
diff --git a/src/main/java/de/bixilon/minosoft/data/entities/entities/player/PlayerEntity.kt b/src/main/java/de/bixilon/minosoft/data/entities/entities/player/PlayerEntity.kt
index e7cdb5828..522642425 100644
--- a/src/main/java/de/bixilon/minosoft/data/entities/entities/player/PlayerEntity.kt
+++ b/src/main/java/de/bixilon/minosoft/data/entities/entities/player/PlayerEntity.kt
@@ -19,7 +19,7 @@ import de.bixilon.minosoft.data.entities.Poses
import de.bixilon.minosoft.data.entities.entities.EntityMetaDataFunction
import de.bixilon.minosoft.data.entities.entities.LivingEntity
import de.bixilon.minosoft.data.player.Arms
-import de.bixilon.minosoft.data.player.PlayerProperty
+import de.bixilon.minosoft.data.player.properties.PlayerProperties
import de.bixilon.minosoft.data.player.tab.TabListItem
import de.bixilon.minosoft.data.registries.entities.EntityType
import de.bixilon.minosoft.data.world.World
@@ -35,7 +35,7 @@ abstract class PlayerEntity(
position: Vec3d = Vec3d.EMPTY,
rotation: EntityRotation = EntityRotation(0.0, 0.0),
name: String = "TBA",
- properties: Map = mapOf(),
+ properties: PlayerProperties = PlayerProperties(),
var tabListItem: TabListItem = TabListItem(name = name, gamemode = Gamemodes.SURVIVAL, properties = properties),
) : LivingEntity(connection, entityType, position, rotation) {
override val dimensions: Vec2
diff --git a/src/main/java/de/bixilon/minosoft/data/entities/entities/player/RemotePlayerEntity.kt b/src/main/java/de/bixilon/minosoft/data/entities/entities/player/RemotePlayerEntity.kt
index be27cfeb7..14c12985f 100644
--- a/src/main/java/de/bixilon/minosoft/data/entities/entities/player/RemotePlayerEntity.kt
+++ b/src/main/java/de/bixilon/minosoft/data/entities/entities/player/RemotePlayerEntity.kt
@@ -15,7 +15,7 @@ package de.bixilon.minosoft.data.entities.entities.player
import de.bixilon.minosoft.data.abilities.Gamemodes
import de.bixilon.minosoft.data.entities.EntityRotation
-import de.bixilon.minosoft.data.player.PlayerProperty
+import de.bixilon.minosoft.data.player.properties.PlayerProperties
import de.bixilon.minosoft.data.player.tab.TabListItem
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.entities.EntityFactory
@@ -30,7 +30,7 @@ class RemotePlayerEntity(
position: Vec3d = Vec3d.EMPTY,
rotation: EntityRotation = EntityRotation(0.0, 0.0),
name: String = "TBA",
- properties: Map = mapOf(),
+ properties: PlayerProperties = PlayerProperties(),
tabListItem: TabListItem = TabListItem(name = name, gamemode = Gamemodes.SURVIVAL, properties = properties),
) : PlayerEntity(connection, entityType, position, rotation, name, properties, tabListItem) {
diff --git a/src/main/java/de/bixilon/minosoft/data/player/PlayerProperty.kt b/src/main/java/de/bixilon/minosoft/data/player/PlayerProperty.kt
deleted file mode 100644
index 5b37de058..000000000
--- a/src/main/java/de/bixilon/minosoft/data/player/PlayerProperty.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Minosoft
- * Copyright (C) 2020 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.data.player
-
-class PlayerProperty(
- val key: String,
- val value: String,
- val signature: String? = null,
-) {
- val isSigned: Boolean
- get() = signature != null // ToDo check signature
-
- override fun toString(): String {
- return "$key: $value"
- }
-}
diff --git a/src/main/java/de/bixilon/minosoft/data/player/properties/PlayerProperties.kt b/src/main/java/de/bixilon/minosoft/data/player/properties/PlayerProperties.kt
new file mode 100644
index 000000000..8aeb56ccd
--- /dev/null
+++ b/src/main/java/de/bixilon/minosoft/data/player/properties/PlayerProperties.kt
@@ -0,0 +1,7 @@
+package de.bixilon.minosoft.data.player.properties
+
+import de.bixilon.minosoft.data.player.properties.textures.PlayerTextures
+
+class PlayerProperties(
+ val textures: PlayerTextures? = null,
+)
diff --git a/src/main/java/de/bixilon/minosoft/data/player/properties/textures/PlayerTexture.kt b/src/main/java/de/bixilon/minosoft/data/player/properties/textures/PlayerTexture.kt
new file mode 100644
index 000000000..70b6bf95e
--- /dev/null
+++ b/src/main/java/de/bixilon/minosoft/data/player/properties/textures/PlayerTexture.kt
@@ -0,0 +1,29 @@
+package de.bixilon.minosoft.data.player.properties.textures
+
+import de.bixilon.minosoft.util.KUtil.check
+import java.net.URL
+
+open class PlayerTexture(
+ val url: URL,
+) {
+ init {
+ url.check()
+
+ check(urlMatches(url, ALLOWED_DOMAINS) && !urlMatches(url, BLOCKED_DOMAINS)) { "URL hostname is not allowed!" }
+ }
+
+
+ companion object {
+ private val ALLOWED_DOMAINS = arrayOf(".minecraft.net", ".mojang.com")
+ private val BLOCKED_DOMAINS = arrayOf("bugs.mojang.com", "education.minecraft.net", "feedback.minecraft.net")
+
+ private fun urlMatches(url: URL, domains: Array): Boolean {
+ for (checkURL in domains) {
+ if (url.host.endsWith(checkURL)) {
+ return true
+ }
+ }
+ return false
+ }
+ }
+}
diff --git a/src/main/java/de/bixilon/minosoft/data/player/properties/textures/PlayerTextures.kt b/src/main/java/de/bixilon/minosoft/data/player/properties/textures/PlayerTextures.kt
new file mode 100644
index 000000000..44cd065b2
--- /dev/null
+++ b/src/main/java/de/bixilon/minosoft/data/player/properties/textures/PlayerTextures.kt
@@ -0,0 +1,38 @@
+package de.bixilon.minosoft.data.player.properties.textures
+
+import com.fasterxml.jackson.module.kotlin.convertValue
+import de.bixilon.minosoft.util.KUtil.toLong
+import de.bixilon.minosoft.util.Util
+import de.bixilon.minosoft.util.YggdrasilUtil
+import de.bixilon.minosoft.util.json.Jackson
+import de.bixilon.minosoft.util.nbt.tag.NBTUtil.compoundCast
+import java.util.*
+
+class PlayerTextures(
+ val name: String?,
+ val uuid: UUID?,
+ val date: Date?,
+ val skin: SkinPlayerTexture?,
+ val cape: PlayerTexture?,
+ val elytra: PlayerTexture?,
+) {
+
+ companion object {
+ fun of(encoded: String, signature: String): PlayerTextures {
+ check(YggdrasilUtil.verify(encoded, signature)) { "Texture signature is invalid!" }
+
+ val json: Map = Jackson.MAPPER.readValue(Base64.getDecoder().decode(encoded), Jackson.JSON_MAP_TYPE)
+
+ // Data also contains `signatureRequired`
+ val textures = json["textures"]?.compoundCast()
+ return PlayerTextures(
+ name = json["profileName"]?.toString(),
+ uuid = json["profileId"]?.toString()?.let { Util.getUUIDFromString(it) },
+ date = json["timestamp"]?.toLong()?.let { Date(it) },
+ skin = textures?.get("SKIN")?.compoundCast()?.let { return@let Jackson.MAPPER.convertValue(it) },
+ cape = textures?.get("CAPE")?.compoundCast()?.let { return@let Jackson.MAPPER.convertValue(it) },
+ elytra = textures?.get("ELYTRA")?.compoundCast()?.let { return@let Jackson.MAPPER.convertValue(it) },
+ )
+ }
+ }
+}
diff --git a/src/main/java/de/bixilon/minosoft/data/player/properties/textures/SkinPlayerTexture.kt b/src/main/java/de/bixilon/minosoft/data/player/properties/textures/SkinPlayerTexture.kt
new file mode 100644
index 000000000..2163d9231
--- /dev/null
+++ b/src/main/java/de/bixilon/minosoft/data/player/properties/textures/SkinPlayerTexture.kt
@@ -0,0 +1,9 @@
+package de.bixilon.minosoft.data.player.properties.textures
+
+import de.bixilon.minosoft.data.player.properties.textures.metadata.SkinMetadata
+import java.net.URL
+
+class SkinPlayerTexture(
+ url: URL,
+ val metadata: SkinMetadata = SkinMetadata(),
+) : PlayerTexture(url = url)
diff --git a/src/main/java/de/bixilon/minosoft/data/player/properties/textures/metadata/SkinMetadata.kt b/src/main/java/de/bixilon/minosoft/data/player/properties/textures/metadata/SkinMetadata.kt
new file mode 100644
index 000000000..d03475d98
--- /dev/null
+++ b/src/main/java/de/bixilon/minosoft/data/player/properties/textures/metadata/SkinMetadata.kt
@@ -0,0 +1,5 @@
+package de.bixilon.minosoft.data.player.properties.textures.metadata
+
+data class SkinMetadata(
+ val model: SkinModel = SkinModel.NORMAL,
+)
diff --git a/src/main/java/de/bixilon/minosoft/data/player/properties/textures/metadata/SkinModel.kt b/src/main/java/de/bixilon/minosoft/data/player/properties/textures/metadata/SkinModel.kt
new file mode 100644
index 000000000..5666cb6c6
--- /dev/null
+++ b/src/main/java/de/bixilon/minosoft/data/player/properties/textures/metadata/SkinModel.kt
@@ -0,0 +1,7 @@
+package de.bixilon.minosoft.data.player.properties.textures.metadata
+
+enum class SkinModel {
+ SLIM,
+ NORMAL,
+ ;
+}
diff --git a/src/main/java/de/bixilon/minosoft/data/player/tab/TabListItem.kt b/src/main/java/de/bixilon/minosoft/data/player/tab/TabListItem.kt
index 95840a0e0..15cad6ad3 100644
--- a/src/main/java/de/bixilon/minosoft/data/player/tab/TabListItem.kt
+++ b/src/main/java/de/bixilon/minosoft/data/player/tab/TabListItem.kt
@@ -14,7 +14,7 @@
package de.bixilon.minosoft.data.player.tab
import de.bixilon.minosoft.data.abilities.Gamemodes
-import de.bixilon.minosoft.data.player.PlayerProperty
+import de.bixilon.minosoft.data.player.properties.PlayerProperties
import de.bixilon.minosoft.data.scoreboard.Team
import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.util.KUtil.nullCompare
@@ -24,7 +24,7 @@ data class TabListItem(
var ping: Int = -1,
var gamemode: Gamemodes = Gamemodes.SURVIVAL,
var displayName: ChatComponent = ChatComponent.of(name),
- var properties: Map = mutableMapOf(),
+ var properties: PlayerProperties = PlayerProperties(),
var team: Team? = null,
) : Comparable {
val tabDisplayName: ChatComponent
diff --git a/src/main/java/de/bixilon/minosoft/data/player/tab/TabListItemData.kt b/src/main/java/de/bixilon/minosoft/data/player/tab/TabListItemData.kt
index 4607b1dc6..68b7c5106 100644
--- a/src/main/java/de/bixilon/minosoft/data/player/tab/TabListItemData.kt
+++ b/src/main/java/de/bixilon/minosoft/data/player/tab/TabListItemData.kt
@@ -14,7 +14,7 @@
package de.bixilon.minosoft.data.player.tab
import de.bixilon.minosoft.data.abilities.Gamemodes
-import de.bixilon.minosoft.data.player.PlayerProperty
+import de.bixilon.minosoft.data.player.properties.PlayerProperties
import de.bixilon.minosoft.data.scoreboard.Team
import de.bixilon.minosoft.data.text.ChatComponent
@@ -24,7 +24,7 @@ data class TabListItemData(
var gamemode: Gamemodes? = null,
var hasDisplayName: Boolean? = null,
var displayName: ChatComponent? = null,
- val properties: Map? = null,
+ val properties: PlayerProperties? = null,
var remove: Boolean = false, // used for legacy tab list
var team: Team? = null,
var removeFromTeam: Boolean = false,
diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/atlas/HUDAtlasManager.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/atlas/HUDAtlasManager.kt
index 19bfdf352..822c432fc 100644
--- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/atlas/HUDAtlasManager.kt
+++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/atlas/HUDAtlasManager.kt
@@ -20,6 +20,8 @@ import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.toVec2i
import de.bixilon.minosoft.util.KUtil.mapCast
import de.bixilon.minosoft.util.KUtil.toInt
import de.bixilon.minosoft.util.KUtil.toResourceLocation
+import glm_.vec2.Vec2
+import glm_.vec2.Vec2i
class HUDAtlasManager(private val hudRenderer: HUDRenderer) {
private lateinit var elements: Map
@@ -79,8 +81,8 @@ class HUDAtlasManager(private val hudRenderer: HUDRenderer) {
fun postInit() {
for (element in elements.values) {
- element.uvStart = element.texture.singlePixelSize * element.start
- element.uvEnd = element.texture.singlePixelSize * element.end
+ element.uvStart = ATLAS_SINGLE_PIXEL_SIZE * element.start
+ element.uvEnd = ATLAS_SINGLE_PIXEL_SIZE * element.end
}
}
@@ -94,5 +96,8 @@ class HUDAtlasManager(private val hudRenderer: HUDRenderer) {
companion object {
private val ATLAS_DATA = "minosoft:mapping/atlas.json".toResourceLocation()
+
+ private val ATLAS_SIZE = Vec2i(256, 256)
+ private val ATLAS_SINGLE_PIXEL_SIZE = Vec2(1.0f) / ATLAS_SIZE
}
}
diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLTextureArray.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLTextureArray.kt
index 736f60dd5..610c7bf9d 100644
--- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLTextureArray.kt
+++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLTextureArray.kt
@@ -198,8 +198,8 @@ class OpenGLTextureArray(
companion object {
- val TEXTURE_RESOLUTION_ID_MAP = intArrayOf(16, 32, 64, 128, 256, 512, 1024) // A 12x12 texture will be saved in texture id 0 (in 0 are only 16x16 textures). Animated textures get split
const val TEXTURE_MAX_RESOLUTION = 1024
+ val TEXTURE_RESOLUTION_ID_MAP = intArrayOf(16, 32, 64, 128, 256, 512, TEXTURE_MAX_RESOLUTION) // A 12x12 texture will be saved in texture id 0 (in 0 are only 16x16 textures). Animated textures get split
const val MAX_MIPMAP_LEVELS = 5
}
}
diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/PlayerEntitySpawnS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/PlayerEntitySpawnS2CP.kt
index 10a79bfbc..3348e1983 100644
--- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/PlayerEntitySpawnS2CP.kt
+++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/PlayerEntitySpawnS2CP.kt
@@ -16,7 +16,7 @@ import de.bixilon.minosoft.data.entities.EntityRotation
import de.bixilon.minosoft.data.entities.entities.player.PlayerEntity
import de.bixilon.minosoft.data.entities.entities.player.RemotePlayerEntity
import de.bixilon.minosoft.data.entities.meta.EntityMetaData
-import de.bixilon.minosoft.data.player.PlayerProperty
+import de.bixilon.minosoft.data.player.properties.PlayerProperties
import de.bixilon.minosoft.modding.event.events.EntitySpawnEvent
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
@@ -38,15 +38,11 @@ class PlayerEntitySpawnS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
entityId = buffer.readVarInt()
var name = "TBA"
- val properties: MutableMap = mutableMapOf()
+ var properties = PlayerProperties()
if (buffer.versionId < ProtocolVersions.V_14W21A) {
name = buffer.readString()
entityUUID = buffer.readUUIDString()
- val length = buffer.readVarInt()
- for (i in 0 until length) {
- val property = PlayerProperty(buffer.readString(), buffer.readString(), buffer.readString())
- properties[property.key] = property
- }
+ properties = buffer.readPlayerProperties()
} else {
entityUUID = buffer.readUUID()
}
diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/TabListDataS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/TabListDataS2CP.kt
index c18df39ac..e62a95390 100644
--- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/TabListDataS2CP.kt
+++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/TabListDataS2CP.kt
@@ -14,7 +14,6 @@ package de.bixilon.minosoft.protocol.packets.s2c.play
import de.bixilon.minosoft.data.abilities.Gamemodes
import de.bixilon.minosoft.data.entities.entities.player.PlayerEntity
-import de.bixilon.minosoft.data.player.PlayerProperty
import de.bixilon.minosoft.data.player.tab.TabListItem
import de.bixilon.minosoft.data.player.tab.TabListItemData
import de.bixilon.minosoft.modding.event.events.TabListEntryChangeEvent
@@ -60,15 +59,7 @@ class TabListDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
when (action) {
TabListItemActions.ADD -> {
val name = buffer.readString()
- val playerProperties: MutableMap = mutableMapOf()
- for (index in 0 until buffer.readVarInt()) {
- val property = PlayerProperty(
- buffer.readString(),
- buffer.readString(),
- buffer.readOptional { buffer.readString() },
- )
- playerProperties[property.key] = property
- }
+ val properties = buffer.readPlayerProperties()
val gamemode = Gamemodes[buffer.readVarInt()]
val ping = buffer.readVarInt()
val hasDisplayName = buffer.readBoolean()
@@ -79,7 +70,7 @@ class TabListDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
}
data = TabListItemData(
name = name,
- properties = playerProperties,
+ properties = properties,
gamemode = gamemode,
ping = ping,
hasDisplayName = hasDisplayName,
diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/PlayInByteBuffer.kt b/src/main/java/de/bixilon/minosoft/protocol/protocol/PlayInByteBuffer.kt
index a215821e9..b173a59d2 100644
--- a/src/main/java/de/bixilon/minosoft/protocol/protocol/PlayInByteBuffer.kt
+++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/PlayInByteBuffer.kt
@@ -14,6 +14,8 @@ package de.bixilon.minosoft.protocol.protocol
import de.bixilon.minosoft.data.entities.meta.EntityMetaData
import de.bixilon.minosoft.data.inventory.ItemStack
+import de.bixilon.minosoft.data.player.properties.PlayerProperties
+import de.bixilon.minosoft.data.player.properties.textures.PlayerTextures
import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.registries.particle.ParticleType
import de.bixilon.minosoft.data.registries.particle.data.BlockParticleData
@@ -214,4 +216,27 @@ class PlayInByteBuffer : InByteBuffer {
fun readEntityIdArray(length: Int = readVarInt()): Array {
return readArray(length) { readEntityId() }
}
+
+
+ fun readPlayerProperties(): PlayerProperties {
+ var textures: PlayerTextures? = null
+ for (i in 0 until readVarInt()) {
+ val name = readString()
+ val value = readString()
+ val signature = if (versionId < V_14W21A) {
+ readString()
+ } else {
+ readOptional { readString() }
+ }
+ when (name) {
+ "textures" -> {
+ check(textures == null) { "Textures duplicated" }
+ textures = PlayerTextures.of(value, signature ?: throw IllegalArgumentException("Texture data needs to be signed!"))
+ }
+ }
+ }
+ return PlayerProperties(
+ textures = textures,
+ )
+ }
}
diff --git a/src/main/java/de/bixilon/minosoft/util/KUtil.kt b/src/main/java/de/bixilon/minosoft/util/KUtil.kt
index b060709df..a3ab6c02a 100644
--- a/src/main/java/de/bixilon/minosoft/util/KUtil.kt
+++ b/src/main/java/de/bixilon/minosoft/util/KUtil.kt
@@ -33,6 +33,7 @@ import glm_.vec4.Vec4t
import sun.misc.Unsafe
import java.io.*
import java.lang.reflect.Field
+import java.net.URL
import java.nio.ByteBuffer
import java.time.Instant
import java.util.*
@@ -558,4 +559,9 @@ object KUtil {
val Locale.fullName: String
get() = language + "_" + country.ifEmpty { language.uppercase() }
+
+
+ fun URL.check() {
+ check(this.protocol == "http" || this.protocol == "https") { "Url is not a web address" }
+ }
}
diff --git a/src/main/java/de/bixilon/minosoft/util/YggdrasilUtil.kt b/src/main/java/de/bixilon/minosoft/util/YggdrasilUtil.kt
new file mode 100644
index 000000000..cbc0b93f3
--- /dev/null
+++ b/src/main/java/de/bixilon/minosoft/util/YggdrasilUtil.kt
@@ -0,0 +1,33 @@
+package de.bixilon.minosoft.util
+
+import de.bixilon.minosoft.Minosoft
+import de.bixilon.minosoft.util.KUtil.toResourceLocation
+import java.security.KeyFactory
+import java.security.PublicKey
+import java.security.Signature
+import java.security.spec.X509EncodedKeySpec
+import java.util.*
+
+
+object YggdrasilUtil {
+ lateinit var PUBLIC_KEY: PublicKey
+ private set
+
+ fun load() {
+ check(!this::PUBLIC_KEY.isInitialized) { "Already loaded!" }
+ val spec = X509EncodedKeySpec(Minosoft.MINOSOFT_ASSETS_MANAGER["minosoft:mojang/yggdrasil_session_pubkey.der".toResourceLocation()].readAllBytes())
+ val keyFactory: KeyFactory = KeyFactory.getInstance("RSA")
+ PUBLIC_KEY = keyFactory.generatePublic(spec)
+ }
+
+ fun verify(data: ByteArray, signature: ByteArray): Boolean {
+ val signatureInstance = Signature.getInstance("SHA1withRSA")
+ signatureInstance.initVerify(PUBLIC_KEY)
+ signatureInstance.update(data)
+ return signatureInstance.verify(signature)
+ }
+
+ fun verify(data: String, signature: String): Boolean {
+ return verify(data.toByteArray(), Base64.getDecoder().decode(signature))
+ }
+}
diff --git a/src/main/java/de/bixilon/minosoft/util/json/Jackson.kt b/src/main/java/de/bixilon/minosoft/util/json/Jackson.kt
index b4c9e0c48..b86a9406e 100644
--- a/src/main/java/de/bixilon/minosoft/util/json/Jackson.kt
+++ b/src/main/java/de/bixilon/minosoft/util/json/Jackson.kt
@@ -2,14 +2,19 @@ package de.bixilon.minosoft.util.json
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationFeature
-import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.databind.MapperFeature
import com.fasterxml.jackson.databind.PropertyNamingStrategies
+import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.databind.type.MapType
import com.fasterxml.jackson.module.kotlin.KotlinFeature
import com.fasterxml.jackson.module.kotlin.KotlinModule
object Jackson {
- val MAPPER = ObjectMapper()
+ val MAPPER = JsonMapper.builder()
+ .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
+ .disable(JsonParser.Feature.AUTO_CLOSE_SOURCE)
+ .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
+ .build()
.registerModule(KotlinModule.Builder()
.withReflectionCacheSize(512)
.configure(KotlinFeature.NullToEmptyCollection, false)
@@ -21,8 +26,6 @@ object Jackson {
.registerModule(ResourceLocationSerializer)
.registerModule(RGBColorSerializer)
.registerModule(ChatComponentColorSerializer)
- .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
- .disable(JsonParser.Feature.AUTO_CLOSE_SOURCE)
.setDefaultMergeable(true)
diff --git a/src/main/java/de/bixilon/minosoft/util/nbt/tag/NBTUtil.kt b/src/main/java/de/bixilon/minosoft/util/nbt/tag/NBTUtil.kt
index 17abb9d09..cf57670d1 100644
--- a/src/main/java/de/bixilon/minosoft/util/nbt/tag/NBTUtil.kt
+++ b/src/main/java/de/bixilon/minosoft/util/nbt/tag/NBTUtil.kt
@@ -13,6 +13,8 @@
package de.bixilon.minosoft.util.nbt.tag
+import de.bixilon.minosoft.util.KUtil.nullCast
+
object NBTUtil {
fun compound(): MutableMap {
@@ -33,11 +35,7 @@ object NBTUtil {
}
fun Any?.compoundCast(): MutableMap? {
- try {
- return this as MutableMap
- } catch (ignored: ClassCastException) {
- }
- return null
+ return this.nullCast()
}
fun Any?.asCompound(): MutableMap {
diff --git a/src/main/java/de/bixilon/minosoft/util/task/worker/StartupTasks.kt b/src/main/java/de/bixilon/minosoft/util/task/worker/StartupTasks.kt
index 80b9ed024..060881820 100644
--- a/src/main/java/de/bixilon/minosoft/util/task/worker/StartupTasks.kt
+++ b/src/main/java/de/bixilon/minosoft/util/task/worker/StartupTasks.kt
@@ -24,5 +24,6 @@ enum class StartupTasks {
INITIALIZE_JAVAFX,
X_START_ON_FIRST_THREAD_WARNING,
FILE_WATCHER,
+ LOAD_YGGDRASIL,
;
}
diff --git a/src/main/resources/assets/minosoft/mojang/yggdrasil_session_pubkey.der b/src/main/resources/assets/minosoft/mojang/yggdrasil_session_pubkey.der
new file mode 100644
index 000000000..9c79a3aa4
Binary files /dev/null and b/src/main/resources/assets/minosoft/mojang/yggdrasil_session_pubkey.der differ