custom deserializer for accounts

That makes the polymorphism dynamic and not at compile time anymore. It allows using proper resource locations (with no namespace) and allows mods to add custom accounts
This commit is contained in:
Moritz Zwerger 2023-10-08 18:40:14 +02:00
parent 3b44fa17c8
commit c0a5018b83
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
7 changed files with 63 additions and 33 deletions

View File

@ -16,6 +16,7 @@ package de.bixilon.minosoft.assets.util
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import de.bixilon.kutil.buffer.BufferDefinition import de.bixilon.kutil.buffer.BufferDefinition
import de.bixilon.kutil.cast.CastUtil.unsafeCast import de.bixilon.kutil.cast.CastUtil.unsafeCast
import de.bixilon.kutil.json.JsonObject
import de.bixilon.mbf.MBFBinaryReader import de.bixilon.mbf.MBFBinaryReader
import de.bixilon.minosoft.util.json.Jackson import de.bixilon.minosoft.util.json.Jackson
import de.matthiasmann.twl.utils.PNGDecoder import de.matthiasmann.twl.utils.PNGDecoder
@ -46,7 +47,7 @@ object InputStreamUtil {
return builder.toString() return builder.toString()
} }
fun InputStream.readJsonObject(close: Boolean = true): Map<String, Any> { fun InputStream.readJsonObject(close: Boolean = true): JsonObject {
try { try {
return Jackson.MAPPER.readValue(this, Jackson.JSON_MAP_TYPE) return Jackson.MAPPER.readValue(this, Jackson.JSON_MAP_TYPE)
} finally { } finally {

View File

@ -14,28 +14,17 @@
package de.bixilon.minosoft.data.accounts package de.bixilon.minosoft.data.accounts
import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonTypeInfo
import de.bixilon.kutil.collections.CollectionUtil.synchronizedMapOf import de.bixilon.kutil.collections.CollectionUtil.synchronizedMapOf
import de.bixilon.kutil.latch.AbstractLatch import de.bixilon.kutil.latch.AbstractLatch
import de.bixilon.kutil.observer.DataObserver.Companion.observed import de.bixilon.kutil.observer.DataObserver.Companion.observed
import de.bixilon.minosoft.config.profile.profiles.account.AccountProfileManager import de.bixilon.minosoft.config.profile.profiles.account.AccountProfileManager
import de.bixilon.minosoft.config.profile.profiles.eros.server.entries.AbstractServer import de.bixilon.minosoft.config.profile.profiles.eros.server.entries.AbstractServer
import de.bixilon.minosoft.data.accounts.types.microsoft.MicrosoftAccount
import de.bixilon.minosoft.data.accounts.types.mojang.MojangAccount
import de.bixilon.minosoft.data.accounts.types.offline.OfflineAccount
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.registries.identified.ResourceLocation import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.util.account.minecraft.MinecraftPrivateKey import de.bixilon.minosoft.util.account.minecraft.MinecraftPrivateKey
import java.util.* import java.util.*
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes(
JsonSubTypes.Type(value = MojangAccount::class, name = "minosoft:mojang_account"),
JsonSubTypes.Type(value = OfflineAccount::class, name = "minosoft:offline_account"),
JsonSubTypes.Type(value = MicrosoftAccount::class, name = "minosoft:microsoft_account"),
)
abstract class Account( abstract class Account(
val username: String, val username: String,
) { ) {

View File

@ -11,20 +11,16 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/ */
package de.bixilon.minosoft.util.json package de.bixilon.minosoft.data.accounts.types
import de.bixilon.kutil.cast.CastUtil.unsafeCast import de.bixilon.minosoft.data.accounts.types.microsoft.MicrosoftAccount
import de.bixilon.minosoft.data.registries.identified.ResourceLocation import de.bixilon.minosoft.data.accounts.types.mojang.MojangAccount
import de.bixilon.minosoft.data.accounts.types.offline.OfflineAccount
object ResourceLocationJsonMap { object AccountTypes {
val types = mutableMapOf(
fun Map<*, *>.toResourceLocationMap(): Map<ResourceLocation, Any> { MicrosoftAccount.identifier to MicrosoftAccount::class,
val ret: MutableMap<ResourceLocation, Any> = mutableMapOf() MojangAccount.identifier to MojangAccount::class,
OfflineAccount.identifier to OfflineAccount::class,
for ((key, value) in this) { )
ret[ResourceLocation.of(key.unsafeCast<String>())] = value.unsafeCast<Any>()
}
return ret
}
} }

View File

@ -17,15 +17,15 @@ import de.bixilon.kutil.cast.CollectionCast.asAnyMap
import de.bixilon.kutil.json.JsonUtil.asJsonObject import de.bixilon.kutil.json.JsonUtil.asJsonObject
import de.bixilon.kutil.primitive.IntUtil.toInt import de.bixilon.kutil.primitive.IntUtil.toInt
import de.bixilon.minosoft.Minosoft import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.assets.util.InputStreamUtil.readJsonObject import de.bixilon.minosoft.assets.util.InputStreamUtil.readJson
import de.bixilon.minosoft.data.entities.entities.Entity import de.bixilon.minosoft.data.entities.entities.Entity
import de.bixilon.minosoft.data.entities.event.events.damage.* import de.bixilon.minosoft.data.entities.event.events.damage.*
import de.bixilon.minosoft.data.registries.entities.DefaultEntityFactories import de.bixilon.minosoft.data.registries.entities.DefaultEntityFactories
import de.bixilon.minosoft.data.registries.factory.DefaultFactory import de.bixilon.minosoft.data.registries.factory.DefaultFactory
import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.protocol.versions.Version import de.bixilon.minosoft.protocol.versions.Version
import de.bixilon.minosoft.util.KUtil.toResourceLocation import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.json.ResourceLocationJsonMap.toResourceLocationMap
import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType import de.bixilon.minosoft.util.logging.LogMessageType
@ -58,7 +58,7 @@ object EntityEvents : DefaultFactory<EntityEvent<*>>(
} }
fun load() { fun load() {
val json = Minosoft.MINOSOFT_ASSETS_MANAGER[FILE].readJsonObject().toResourceLocationMap() val json: Map<ResourceLocation, Any> = Minosoft.MINOSOFT_ASSETS_MANAGER[FILE].readJson()
for ((name, data) in json) { for ((name, data) in json) {
val clazz = DefaultEntityFactories.ABSTRACT_ENTITY_DATA_CLASSES[name]?.java ?: DefaultEntityFactories[name]?.javaClass // TODO: This is the companion class val clazz = DefaultEntityFactories.ABSTRACT_ENTITY_DATA_CLASSES[name]?.java ?: DefaultEntityFactories[name]?.javaClass // TODO: This is the companion class
if (clazz == null) { if (clazz == null) {

View File

@ -15,7 +15,7 @@ package de.bixilon.minosoft.data.registries.fallback
import de.bixilon.kutil.json.JsonUtil.asJsonObject import de.bixilon.kutil.json.JsonUtil.asJsonObject
import de.bixilon.minosoft.Minosoft import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.assets.util.InputStreamUtil.readJsonObject import de.bixilon.minosoft.assets.util.InputStreamUtil.readJson
import de.bixilon.minosoft.data.container.equipment.EquipmentSlots import de.bixilon.minosoft.data.container.equipment.EquipmentSlots
import de.bixilon.minosoft.data.entities.EntityAnimations import de.bixilon.minosoft.data.entities.EntityAnimations
import de.bixilon.minosoft.data.entities.EntityObjectType import de.bixilon.minosoft.data.entities.EntityObjectType
@ -34,7 +34,6 @@ import de.bixilon.minosoft.data.registries.registries.registry.Registry
import de.bixilon.minosoft.data.registries.registries.registry.ResourceLocationRegistry import de.bixilon.minosoft.data.registries.registries.registry.ResourceLocationRegistry
import de.bixilon.minosoft.protocol.packets.c2s.play.entity.EntityActionC2SP import de.bixilon.minosoft.protocol.packets.c2s.play.entity.EntityActionC2SP
import de.bixilon.minosoft.protocol.packets.s2c.play.title.TitleS2CF import de.bixilon.minosoft.protocol.packets.s2c.play.title.TitleS2CF
import de.bixilon.minosoft.util.json.ResourceLocationJsonMap.toResourceLocationMap
import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType import de.bixilon.minosoft.util.logging.LogMessageType
@ -77,7 +76,7 @@ object FallbackRegistries {
check(!initialized) { "Already initialized!" } check(!initialized) { "Already initialized!" }
Log.log(LogMessageType.OTHER, LogLevels.VERBOSE) { "Loading default registries..." } Log.log(LogMessageType.OTHER, LogLevels.VERBOSE) { "Loading default registries..." }
val enumJson = Minosoft.MINOSOFT_ASSETS_MANAGER[ENUM_RESOURCE_LOCATION].readJsonObject().toResourceLocationMap() val enumJson: Map<ResourceLocation, Any> = Minosoft.MINOSOFT_ASSETS_MANAGER[ENUM_RESOURCE_LOCATION].readJson()
EQUIPMENT_SLOTS_REGISTRY.initialize(enumJson[ResourceLocation.of("equipment_slots")].asJsonObject()) EQUIPMENT_SLOTS_REGISTRY.initialize(enumJson[ResourceLocation.of("equipment_slots")].asJsonObject())
HAND_EQUIPMENT_SLOTS_REGISTRY.initialize(enumJson[ResourceLocation.of("hand_equipment_slots")].asJsonObject()) HAND_EQUIPMENT_SLOTS_REGISTRY.initialize(enumJson[ResourceLocation.of("hand_equipment_slots")].asJsonObject())
@ -92,7 +91,7 @@ object FallbackRegistries {
ENTITY_ACTIONS_REGISTRY.initialize(enumJson[ResourceLocation.of("entity_actions")].asJsonObject()) ENTITY_ACTIONS_REGISTRY.initialize(enumJson[ResourceLocation.of("entity_actions")].asJsonObject())
val registriesJson = Minosoft.MINOSOFT_ASSETS_MANAGER[REGISTRIES_RESOURCE_LOCATION].readJsonObject().toResourceLocationMap() val registriesJson: Map<ResourceLocation, Any> = Minosoft.MINOSOFT_ASSETS_MANAGER[REGISTRIES_RESOURCE_LOCATION].readJson()
DEFAULT_PLUGIN_CHANNELS_REGISTRY.initialize(registriesJson[ResourceLocation.of("default_channels")].asJsonObject(), PluginChannel) DEFAULT_PLUGIN_CHANNELS_REGISTRY.initialize(registriesJson[ResourceLocation.of("default_channels")].asJsonObject(), PluginChannel)

View File

@ -0,0 +1,44 @@
/*
* 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.util.json
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
import com.fasterxml.jackson.databind.module.SimpleModule
import de.bixilon.kutil.json.JsonObject
import de.bixilon.minosoft.data.accounts.Account
import de.bixilon.minosoft.data.accounts.types.AccountTypes
import de.bixilon.minosoft.util.KUtil.toResourceLocation
object AccountDeserializer : SimpleModule() {
init {
addDeserializer(Account::class.java, Deserializer)
}
object Deserializer : StdDeserializer<Account>(Account::class.java) {
override fun deserialize(parser: JsonParser, context: DeserializationContext?): Account {
val codec = parser.codec
val root = codec.readValue<JsonObject>(parser, Jackson.JSON_MAP_TYPE)
val type = root["type"].toResourceLocation()
val clazz = AccountTypes.types[type] ?: throw IllegalArgumentException("Can not find account type $type!")
return Jackson.MAPPER.convertValue(root, clazz.java)
}
}
}

View File

@ -46,6 +46,7 @@ object Jackson {
) )
.registerModule(JavaTimeModule()) .registerModule(JavaTimeModule())
.registerModule(ResourceLocationSerializer) .registerModule(ResourceLocationSerializer)
.registerModule(AccountDeserializer)
.registerModule(RGBColorSerializer) .registerModule(RGBColorSerializer)
.registerModule(ChatComponentColorSerializer) .registerModule(ChatComponentColorSerializer)
.registerModule(Vec2Serializer) .registerModule(Vec2Serializer)