wip improved entity data mapping

This commit is contained in:
Bixilon 2022-04-25 11:18:10 +02:00
parent 139cf31736
commit f31d937b14
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
11 changed files with 147 additions and 34 deletions

View File

@ -14,7 +14,6 @@
package de.bixilon.minosoft.data.entities.data
import de.bixilon.kotlinglm.vec3.Vec3i
import de.bixilon.kutil.cast.CastUtil.unsafeCast
import de.bixilon.kutil.concurrent.lock.simple.SimpleLock
import de.bixilon.minosoft.data.container.stack.ItemStack
import de.bixilon.minosoft.data.direction.Directions
@ -61,23 +60,48 @@ class EntityData(
return sets.toString()
}
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE")
inline fun <reified K> get(field: EntityDataField, default: K?): K? {
lock.acquire()
try {
val type = connection.registries.getEntityDataIndex(field) ?: return default // field is not present (in this version)
val data = this.data[type] ?: return default
if (data !is K) {
Log.log(LogMessageType.OTHER, LogLevels.VERBOSE) { "Entity data $data can not be casted to ${K::class}" }
return default
}
return data
} finally {
lock.release()
}
}
fun getBoolean(field: EntityDataField, default: Boolean): Boolean {
val data: Any = this.get(field, default) ?: return default
if (data is Boolean) {
return data
}
if (data is Number) {
return data == 0x01
}
Log.log(LogMessageType.OTHER, LogLevels.VERBOSE) { "Invalid boolean $data" }
return default
}
fun getBitMask(field: EntityDataField, bitMask: Int, default: Byte): Boolean {
val byte: Byte = get(field, default) ?: default
return BitByte.isBitMask(byte.toInt(), bitMask)
}
fun getChatComponent(field: EntityDataField, default: Any?): ChatComponent {
return ChatComponent.of(get(field, default))
}
@Deprecated("refactor")
inner class EntityDataHashMap : Int2ObjectOpenHashMap<Any>() {
inline operator fun <reified K> get(field: EntityDataFields): K {
lock.acquire()
try {
val index: Int = this@EntityData.connection.registries.getEntityMetaDataIndex(field) ?: return field.defaultValue.unsafeCast() // Can not find field.
get(index)?.let {
try {
return it as K
} catch (exception: ClassCastException) {
Log.log(LogMessageType.OTHER, level = LogLevels.WARN, message = exception)
}
}
return field.defaultValue as K
} finally {
lock.release()
}
throw TODO()
}
fun getPose(field: EntityDataFields): Poses? {

View File

@ -0,0 +1,18 @@
/*
* Minosoft
* Copyright (C) 2020-2022 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.data.entities.data
class EntityDataField(val names: List<String>) {
constructor(name: String) : this(listOf(name))
}

View File

@ -29,6 +29,7 @@ import de.bixilon.minosoft.data.entities.EntityRotation
import de.bixilon.minosoft.data.entities.Poses
import de.bixilon.minosoft.data.entities.StatusEffectInstance
import de.bixilon.minosoft.data.entities.data.EntityData
import de.bixilon.minosoft.data.entities.data.EntityDataField
import de.bixilon.minosoft.data.entities.entities.player.PlayerEntity
import de.bixilon.minosoft.data.entities.entities.vehicle.boat.Boat
import de.bixilon.minosoft.data.physics.PhysicsEntity
@ -207,7 +208,7 @@ abstract class Entity(
}
private fun getEntityFlag(bitMask: Int): Boolean {
return data.sets.getBitMask(EntityDataFields.ENTITY_FLAGS, bitMask)
return data.getBitMask(FLAGS_DATA, bitMask, 0x00)
}
@get:SynchronizedEntityData(name = "On fire")
@ -634,6 +635,14 @@ abstract class Entity(
companion object {
private val FLAGS_DATA = EntityDataField("ENTITY_FLAGS")
private val AIR_SUPPLY_DATA = EntityDataField("ENTITY_AIR_SUPPLY")
private val CUSTOM_NAME_DATA = EntityDataField("ENTITY_CUSTOM_NAME")
private val CUSTOM_NAME_VISIBLE_DATA = EntityDataField("ENTITY_CUSTOM_NAME_VISIBLE")
private val SILENT_DATA = EntityDataField("ENTITY_SILENT")
private val NO_GRAVITY_DATA = EntityDataField("ENTITY_NO_GRAVITY")
private val POSE_DATA = EntityDataField("ENTITY_POSE")
private val TICKS_FROZEN_DATA = EntityDataField("ENTITY_TICKS_FROZEN")
private val BELOW_POSITION_MINUS = Vec3(0, 0.2f, 0)
}
}

View File

@ -40,6 +40,7 @@ import de.bixilon.minosoft.data.container.stack.ItemStack
import de.bixilon.minosoft.data.container.types.PlayerInventory
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.entities.EntityRotation
import de.bixilon.minosoft.data.entities.data.EntityData
import de.bixilon.minosoft.data.entities.entities.player.PlayerEntity
import de.bixilon.minosoft.data.entities.entities.player.RemotePlayerEntity
import de.bixilon.minosoft.data.physics.PhysicsConstants
@ -75,7 +76,7 @@ import kotlin.math.pow
class LocalPlayerEntity(
account: Account,
connection: PlayConnection,
) : PlayerEntity(connection, connection.registries.entityTypeRegistry[RemotePlayerEntity.RESOURCE_LOCATION]!!, Vec3d.EMPTY, EntityRotation(0.0, 0.0), account.username) {
) : PlayerEntity(connection, connection.registries.entityTypeRegistry[RemotePlayerEntity.RESOURCE_LOCATION]!!, EntityData(connection), Vec3d.EMPTY, EntityRotation(0.0, 0.0), account.username) {
val healthCondition = PlayerHealthCondition()
val experienceCondition = PlayerExperienceCondition()
var spawnPosition: Vec3i = Vec3i.EMPTY

View File

@ -31,9 +31,11 @@ import de.bixilon.minosoft.data.entities.entities.item.FallingBlockEntity
import de.bixilon.minosoft.data.entities.entities.item.ItemEntity
import de.bixilon.minosoft.data.entities.entities.item.PrimedTNT
import de.bixilon.minosoft.data.entities.entities.monster.*
import de.bixilon.minosoft.data.entities.entities.monster.piglin.AbstractPiglin
import de.bixilon.minosoft.data.entities.entities.monster.piglin.Piglin
import de.bixilon.minosoft.data.entities.entities.monster.piglin.PiglinBrute
import de.bixilon.minosoft.data.entities.entities.monster.raid.*
import de.bixilon.minosoft.data.entities.entities.npc.villager.AbstractVillager
import de.bixilon.minosoft.data.entities.entities.npc.villager.Villager
import de.bixilon.minosoft.data.entities.entities.npc.villager.WanderingTrader
import de.bixilon.minosoft.data.entities.entities.player.RemotePlayerEntity
@ -44,6 +46,7 @@ import de.bixilon.minosoft.data.entities.entities.vehicle.boat.ChestBoat
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.factory.DefaultFactory
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.util.KUtil.toResourceLocation
@SuppressWarnings("deprecation")
object DefaultEntityFactories : DefaultFactory<EntityFactory<*>>(
@ -181,4 +184,23 @@ object DefaultEntityFactories : DefaultFactory<EntityFactory<*>>(
val tweakedEntityType = connection.registries.entityTypeRegistry[tweakedResourceLocation] ?: throw UnknownEntityException("Can not find tweaked entity type data in ${connection.version}: $tweakedResourceLocation for $factory")
return tweakedFactory.build(connection, tweakedEntityType, data ?: EntityData(connection), position, rotation)
}
val ABSTRACT_ENTITY_META_CLASSES = mapOf(
"Entity".toResourceLocation() to Entity::class,
"LivingEntity".toResourceLocation() to LivingEntity::class,
"PersistentProjectileEntity".toResourceLocation() to Projectile::class,
"MobEntity".toResourceLocation() to Mob::class,
"PassiveEntity".toResourceLocation() to AgeableMob::class,
"TameableEntity".toResourceLocation() to TamableAnimal::class,
"FishEntity".toResourceLocation() to AbstractFish::class,
"AbstractDonkeyEntity".toResourceLocation() to AbstractChestedHorse::class,
"HorseBaseEntity".toResourceLocation() to AbstractHorse::class,
"SpellcastingIllagerEntity".toResourceLocation() to SpellcasterIllager::class,
"RaiderEntity".toResourceLocation() to Raider::class,
"AbstractFireballEntity".toResourceLocation() to Fireball::class,
"AbstractMinecartEntity".toResourceLocation() to AbstractMinecart::class,
"AbstractPiglinEntity".toResourceLocation() to AbstractPiglin::class,
"ThrownItemEntity".toResourceLocation() to ThrowableItemProjectile::class,
"MerchantEntity".toResourceLocation() to AbstractVillager::class,
)
}

View File

@ -18,9 +18,10 @@ import de.bixilon.kutil.cast.CastUtil.nullCast
import de.bixilon.kutil.cast.CastUtil.unsafeCast
import de.bixilon.kutil.json.JsonUtil.toJsonObject
import de.bixilon.kutil.primitive.BooleanUtil.toBoolean
import de.bixilon.minosoft.data.entities.EntityDataFields
import de.bixilon.kutil.primitive.IntUtil.toInt
import de.bixilon.minosoft.data.entities.EntityRotation
import de.bixilon.minosoft.data.entities.data.EntityData
import de.bixilon.minosoft.data.entities.data.EntityDataField
import de.bixilon.minosoft.data.entities.entities.Entity
import de.bixilon.minosoft.data.language.Translatable
import de.bixilon.minosoft.data.registries.ResourceLocation
@ -31,7 +32,10 @@ import de.bixilon.minosoft.data.registries.registries.registry.ResourceLocationD
import de.bixilon.minosoft.datafixer.EntityAttributeFixer.fix
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import java.util.*
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
import java.lang.reflect.Modifier
data class EntityType(
override val resourceLocation: ResourceLocation,
@ -45,7 +49,6 @@ data class EntityType(
val spawnEgg: SpawnEggItem?,
) : RegistryItem(), Translatable {
override fun toString(): String {
return resourceLocation.toString()
}
@ -57,11 +60,35 @@ data class EntityType(
companion object : ResourceLocationDeserializer<EntityType> {
override fun deserialize(registries: Registries?, resourceLocation: ResourceLocation, data: Map<String, Any>): EntityType? {
check(registries != null) { "Registries is null!" }
val factory = DefaultEntityFactories[resourceLocation]
data["meta"]?.toJsonObject()?.let {
for ((minosoftFieldName, index) in it) {
val minosoftField = EntityDataFields[minosoftFieldName.lowercase(Locale.getDefault())]
registries.entityMetaIndexMap[minosoftField] = index.unsafeCast()
val fields: MutableMap<String, EntityDataField> = mutableMapOf()
val metaClass = factory?.javaClass ?: DefaultEntityFactories.ABSTRACT_ENTITY_META_CLASSES[resourceLocation]?.java
if (metaClass == null) {
Log.log(LogMessageType.VERSION_LOADING, LogLevels.VERBOSE) { "Can not find class for entity data ($resourceLocation)" }
return@let
}
for (field in metaClass.declaredFields) {
if (!Modifier.isStatic(field.modifiers)) {
continue
}
if (field.type != EntityDataField::class.java) {
continue
}
field.isAccessible = true
val dataField = field.get(null) as EntityDataField
for (name in dataField.names) {
fields[name] = dataField
}
}
for ((fieldName, index) in it) {
val fieldType = fields[fieldName]
if (fieldType == null) {
Log.log(LogMessageType.VERSION_LOADING, LogLevels.VERBOSE) { "Can not find entity data $fieldName for $resourceLocation" }
continue
}
registries.entityDataIndexMap[fieldType] = index.toInt()
}
}
if (data["width"] == null) {
@ -69,6 +96,10 @@ data class EntityType(
return null
}
if (factory == null) {
throw NullPointerException("Can not find entity factory for $resourceLocation")
}
val attributes: MutableMap<ResourceLocation, Double> = mutableMapOf()
data["attributes"]?.toJsonObject()?.let {
@ -85,7 +116,7 @@ data class EntityType(
fireImmune = data["fire_immune"]?.toBoolean() ?: false,
sizeFixed = data["size_fixed"]?.toBoolean() ?: false,
attributes = attributes.toMap(),
factory = DefaultEntityFactories[resourceLocation] ?: error("Can not find entity factory for $resourceLocation"),
factory = factory,
spawnEgg = registries.itemRegistry[data["spawn_egg_item"]]?.nullCast(), // ToDo: Not yet in PixLyzer
)
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
* Copyright (C) 2020-2022 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.
*
@ -17,7 +17,7 @@ import de.bixilon.minosoft.data.registries.CompanionResourceLocation
import de.bixilon.minosoft.data.registries.MultiResourceLocationAble
import de.bixilon.minosoft.data.registries.ResourceLocation
open class DefaultFactory<T : CompanionResourceLocation>(vararg factories: T) {
open class DefaultFactory<T : CompanionResourceLocation>(vararg factories: T) : Iterable<T> {
private val factories = factories
private val factoryMap: Map<ResourceLocation, T>
@ -46,4 +46,8 @@ open class DefaultFactory<T : CompanionResourceLocation>(vararg factories: T) {
operator fun get(index: Int): T {
return factories[index]
}
override fun iterator(): Iterator<T> {
return factories.iterator()
}
}

View File

@ -16,8 +16,8 @@ import de.bixilon.kutil.cast.CastUtil.nullCast
import de.bixilon.kutil.cast.CastUtil.unsafeCast
import de.bixilon.kutil.json.JsonUtil.toJsonObject
import de.bixilon.minosoft.data.container.InventorySlots
import de.bixilon.minosoft.data.entities.EntityDataFields
import de.bixilon.minosoft.data.entities.block.BlockDataDataType
import de.bixilon.minosoft.data.entities.data.EntityDataField
import de.bixilon.minosoft.data.entities.data.types.EntityDataDataTypes
import de.bixilon.minosoft.data.registries.*
import de.bixilon.minosoft.data.registries.biomes.Biome
@ -92,7 +92,7 @@ class Registries {
val blockStateRegistry = BlockStateRegistry(false)
val entityMetaIndexMap: MutableMap<EntityDataFields, Int> = mutableMapOf()
val entityDataIndexMap: MutableMap<EntityDataField, Int> = mutableMapOf()
val entityTypeRegistry: Registry<EntityType> = Registry()
val blockEntityTypeRegistry = BlockEntityTypeRegistry()
@ -117,8 +117,8 @@ class Registries {
}
}
fun getEntityMetaDataIndex(field: EntityDataFields): Int? {
return entityMetaIndexMap[field] ?: parentRegistries?.getEntityMetaDataIndex(field)
fun getEntityDataIndex(field: EntityDataField): Int? {
return entityDataIndexMap[field] ?: parentRegistries?.getEntityDataIndex(field)
}
private fun <T : Enum<*>> loadEnumRegistry(version: Version, data: Any?, registry: EnumRegistry<T>, alternative: PerVersionEnumRegistry<T>) {

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.protocol.packets.s2c.play.entity.spawn
import de.bixilon.kotlinglm.vec3.Vec3d
import de.bixilon.minosoft.data.entities.data.EntityData
import de.bixilon.minosoft.data.entities.entities.ExperienceOrb
import de.bixilon.minosoft.modding.event.events.EntitySpawnEvent
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
@ -30,6 +31,7 @@ class EntityExperienceOrbS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
val entity: ExperienceOrb = ExperienceOrb(
connection = buffer.connection,
entityType = buffer.connection.registries.entityTypeRegistry[ExperienceOrb.RESOURCE_LOCATION]!!,
data = EntityData(buffer.connection),
position = if (buffer.versionId < ProtocolVersions.V_16W06A) {
Vec3d(buffer.readFixedPointNumberInt(), buffer.readFixedPointNumberInt(), buffer.readFixedPointNumberInt())
} else {

View File

@ -15,6 +15,7 @@ package de.bixilon.minosoft.protocol.packets.s2c.play.entity.spawn
import de.bixilon.kotlinglm.vec3.Vec3i
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.direction.Directions.Companion.byId
import de.bixilon.minosoft.data.entities.data.EntityData
import de.bixilon.minosoft.data.entities.entities.decoration.Painting
import de.bixilon.minosoft.data.registries.Motive
import de.bixilon.minosoft.modding.event.events.EntitySpawnEvent
@ -54,7 +55,7 @@ class EntityPaintingS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
position = buffer.readBlockPosition()
direction = byId(buffer.readUnsignedByte())
}
entity = Painting(buffer.connection, buffer.connection.registries.entityTypeRegistry[Painting.RESOURCE_LOCATION]!!, position, direction, motive!!)
entity = Painting(buffer.connection, buffer.connection.registries.entityTypeRegistry[Painting.RESOURCE_LOCATION]!!, EntityData(buffer.connection), position, direction, motive!!)
}
override fun handle(connection: PlayConnection) {

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.protocol.packets.s2c.play.entity.spawn
import de.bixilon.kotlinglm.vec3.Vec3d
import de.bixilon.minosoft.data.entities.data.EntityData
import de.bixilon.minosoft.data.entities.entities.LightningBolt
import de.bixilon.minosoft.modding.event.events.EntitySpawnEvent
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
@ -27,17 +28,17 @@ import de.bixilon.minosoft.util.logging.LogMessageType
@LoadPacket(threadSafe = false)
class GlobalEntitySpawnS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
val entityId: Int = buffer.readVarInt()
val type = buffer.readByte()
val entity: LightningBolt
init {
val type = buffer.readByte()
val position: Vec3d = if (buffer.versionId < ProtocolVersions.V_16W06A) {
Vec3d(buffer.readFixedPointNumberInt(), buffer.readFixedPointNumberInt(), buffer.readFixedPointNumberInt())
} else {
buffer.readVec3d()
}
entity = LightningBolt(buffer.connection, buffer.connection.registries.entityTypeRegistry[LightningBolt.RESOURCE_LOCATION]!!, position)
entity = LightningBolt(buffer.connection, buffer.connection.registries.entityTypeRegistry[LightningBolt.RESOURCE_LOCATION]!!, EntityData(buffer.connection), position)
}
override fun handle(connection: PlayConnection) {
@ -47,6 +48,6 @@ class GlobalEntitySpawnS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
}
override fun log(reducedLog: Boolean) {
Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "Global entity spawn (entityId=$entity, entity=$entity)" }
Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "Global entity spawn (entityId=$entity, type=$type, entity=$entity)" }
}
}