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

View File

@ -14,28 +14,17 @@
package de.bixilon.minosoft.data.accounts
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.latch.AbstractLatch
import de.bixilon.kutil.observer.DataObserver.Companion.observed
import de.bixilon.minosoft.config.profile.profiles.account.AccountProfileManager
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.registries.identified.ResourceLocation
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.util.account.minecraft.MinecraftPrivateKey
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(
val username: String,
) {

View File

@ -11,20 +11,16 @@
* 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.registries.identified.ResourceLocation
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
object ResourceLocationJsonMap {
fun Map<*, *>.toResourceLocationMap(): Map<ResourceLocation, Any> {
val ret: MutableMap<ResourceLocation, Any> = mutableMapOf()
for ((key, value) in this) {
ret[ResourceLocation.of(key.unsafeCast<String>())] = value.unsafeCast<Any>()
}
return ret
}
object AccountTypes {
val types = mutableMapOf(
MicrosoftAccount.identifier to MicrosoftAccount::class,
MojangAccount.identifier to MojangAccount::class,
OfflineAccount.identifier to OfflineAccount::class,
)
}

View File

@ -17,15 +17,15 @@ import de.bixilon.kutil.cast.CollectionCast.asAnyMap
import de.bixilon.kutil.json.JsonUtil.asJsonObject
import de.bixilon.kutil.primitive.IntUtil.toInt
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.event.events.damage.*
import de.bixilon.minosoft.data.registries.entities.DefaultEntityFactories
import de.bixilon.minosoft.data.registries.factory.DefaultFactory
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.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.json.ResourceLocationJsonMap.toResourceLocationMap
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
@ -58,7 +58,7 @@ object EntityEvents : DefaultFactory<EntityEvent<*>>(
}
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) {
val clazz = DefaultEntityFactories.ABSTRACT_ENTITY_DATA_CLASSES[name]?.java ?: DefaultEntityFactories[name]?.javaClass // TODO: This is the companion class
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.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.entities.EntityAnimations
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.protocol.packets.c2s.play.entity.EntityActionC2SP
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.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
@ -77,7 +76,7 @@ object FallbackRegistries {
check(!initialized) { "Already initialized!" }
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())
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())
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)

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(ResourceLocationSerializer)
.registerModule(AccountDeserializer)
.registerModule(RGBColorSerializer)
.registerModule(ChatComponentColorSerializer)
.registerModule(Vec2Serializer)