fix nbt writing, item picking, ItemStack: fix enchantment nbt generation

This commit is contained in:
Bixilon 2021-10-31 14:29:45 +01:00
parent 0a9091b73b
commit b3da8b1185
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
14 changed files with 135 additions and 29 deletions

View File

@ -20,6 +20,7 @@ import glm_.vec3.Vec3i
abstract class BlockEntity(
val connection: PlayConnection,
) {
open val nbt: Map<String, Any> = mapOf()
open fun updateNBT(nbt: Map<String, Any>) = Unit

View File

@ -240,10 +240,11 @@ class ItemStack(
nbt[HIDE_FLAGS_TAG] = hideFlags
}
if (enchantments.isNotEmpty()) {
val connection = connection!!
val enchantmentList: MutableList<Map<String, Any>> = mutableListOf()
for ((enchantment, level) in enchantments) {
val enchantmentTag: MutableMap<String, Any> = mutableMapOf()
enchantmentTag[ENCHANTMENT_ID_TAG] = if (connection!!.version.isFlattened()) {
enchantmentTag[ENCHANTMENT_ID_TAG] = if (connection.version.isFlattened()) {
enchantment.resourceLocation.full
} else {
connection.registries.enchantmentRegistry.getId(enchantment)
@ -254,8 +255,9 @@ class ItemStack(
} else {
level.toShort()
}
enchantmentList += enchantmentTag
}
if (connection!!.version.isFlattened()) {
if (connection.version.isFlattened()) {
nbt[ENCHANTMENT_FLATTENING_TAG] = enchantmentList
} else {
nbt[ENCHANTMENT_PRE_FLATTENING_TAG] = enchantmentList

View File

@ -17,7 +17,7 @@ data class Abilities(
var isInvulnerable: Boolean = false,
var isFlying: Boolean = false,
var canFly: Boolean = false,
var canInstantBreak: Boolean = false, // ToDo: This is the check if we are in creative mode, but no exactly sure...
var creative: Boolean = false, // ToDo: This is the check if we are in creative mode, but no exactly sure...
var flyingSpeed: Double = 0.05,
var walkingSpeed: Double = 0.1,

View File

@ -198,7 +198,6 @@ class LocalPlayerEntity(
val swimHeight: Double
get() = (eyeHeight < 0.4).decide(0.0, 0.4)
val reachDistance: Double
get() = (gamemode == Gamemodes.CREATIVE).decide(5.0, 4.5)

View File

@ -19,12 +19,14 @@ import de.bixilon.minosoft.data.entities.EntityRotation
import de.bixilon.minosoft.data.entities.entities.Entity
import de.bixilon.minosoft.data.entities.meta.EntityMetaData
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.items.SpawnEggItem
import de.bixilon.minosoft.data.registries.registries.Registries
import de.bixilon.minosoft.data.registries.registries.registry.RegistryItem
import de.bixilon.minosoft.data.registries.registries.registry.ResourceLocationDeserializer
import de.bixilon.minosoft.data.registries.registries.registry.Translatable
import de.bixilon.minosoft.datafixer.EntityAttributeFixer.fix
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.util.KUtil.nullCast
import de.bixilon.minosoft.util.KUtil.toBoolean
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.KUtil.unsafeCast
@ -41,8 +43,10 @@ data class EntityType(
val fireImmune: Boolean,
val attributes: Map<ResourceLocation, Double>,
val factory: EntityFactory<out Entity>,
val spawnEgg: SpawnEggItem?,
) : RegistryItem(), Translatable {
override fun toString(): String {
return resourceLocation.toString()
}
@ -83,6 +87,7 @@ data class EntityType(
sizeFixed = data["size_fixed"]?.toBoolean() ?: false,
attributes = attributes.toMap(),
factory = DefaultEntityFactories[resourceLocation] ?: error("Can not find entity factory for $resourceLocation"),
spawnEgg = registries.itemRegistry[data["spawn_egg_item"]]?.nullCast(), // ToDo: Not yet in PixLyzer
)
}
}

View File

@ -40,7 +40,7 @@ class PlayerInventory(connection: PlayConnection) : Container(
fun getHotbarSlot(hotbarSlot: Int = connection.player.selectedHotbarSlot): ItemStack? {
check(hotbarSlot in 0..HOTBAR_SLOTS) { "Hotbar slot out of bounds!" }
return slots[hotbarSlot + HOTBAR_OFFSET] // ToDo
return slots[hotbarSlot + HOTBAR_OFFSET]
}
operator fun get(slot: InventorySlots.EquipmentSlots): ItemStack? {

View File

@ -23,6 +23,7 @@ import de.bixilon.minosoft.protocol.protocol.PacketTypes.C2S
import de.bixilon.minosoft.protocol.protocol.PacketTypes.S2C
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.protocol.protocol.ProtocolStates
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_15W31A
import de.bixilon.minosoft.util.CountUpAndDownLatch
import de.bixilon.minosoft.util.KUtil.decide
import de.bixilon.minosoft.util.logging.Log
@ -152,4 +153,6 @@ data class Version(
override fun toString(): String {
return name
}
val hasOffhand = versionId >= V_15W31A
}

View File

@ -145,7 +145,7 @@ class LeftClickHandler(
val canStartBreaking = currentTime - breakSent >= ProtocolDefinition.TICK_TIME
val canInstantBreak = connection.player.baseAbilities.canInstantBreak || connection.player.gamemode == Gamemodes.CREATIVE
val canInstantBreak = connection.player.baseAbilities.creative || connection.player.gamemode == Gamemodes.CREATIVE
if (canInstantBreak) {
if (!canStartBreaking) {

View File

@ -35,8 +35,8 @@ class HotbarInteractionHandler(
val renderWindow: RenderWindow,
) {
private val connection = renderWindow.connection
private val slotLimiter = RateLimiter()
private val swapLimiter = RateLimiter(dependencies = synchronizedSetOf(slotLimiter)) // we don't want to swap wrong items
val slotLimiter = RateLimiter()
val swapLimiter = RateLimiter(dependencies = synchronizedSetOf(slotLimiter)) // we don't want to swap wrong items
private var currentScrollOffset = 0.0
@ -54,7 +54,7 @@ class HotbarInteractionHandler(
}
fun swapItems() {
if (connection.player.gamemode == Gamemodes.SPECTATOR) {
if (!connection.version.hasOffhand || connection.player.gamemode == Gamemodes.SPECTATOR) {
return
}
val inventory = connection.player.inventory
@ -62,6 +62,7 @@ class HotbarInteractionHandler(
val off = inventory[InventorySlots.EquipmentSlots.OFF_HAND]
if (main == null && off == null) {
// ToDo: Forbid swap if both are equals?
// both are air, we can't swap
return
}

View File

@ -19,12 +19,15 @@ class InteractionManager(
val renderWindow: RenderWindow,
) {
val hotbar = HotbarInteractionHandler(renderWindow)
val pick = ItemPickInteractionHandler(renderWindow, this)
fun init() {
hotbar.init()
pick.init()
}
fun draw(delta: Double) {
hotbar.draw(delta)
pick.draw(delta)
}
}

View File

@ -0,0 +1,99 @@
/*
* Minosoft
* Copyright (C) 2021 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.input.interaction
import de.bixilon.minosoft.config.key.KeyAction
import de.bixilon.minosoft.config.key.KeyBinding
import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.data.inventory.InventorySlots
import de.bixilon.minosoft.data.inventory.ItemStack
import de.bixilon.minosoft.data.registries.other.containers.PlayerInventory
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.input.camera.hit.BlockRaycastHit
import de.bixilon.minosoft.gui.rendering.input.camera.hit.EntityRaycastHit
import de.bixilon.minosoft.protocol.RateLimiter
import de.bixilon.minosoft.protocol.packets.c2s.play.ItemStackCreateC2SP
import de.bixilon.minosoft.util.KUtil.toResourceLocation
class ItemPickInteractionHandler(
val renderWindow: RenderWindow,
val interactionManager: InteractionManager,
) {
private val connection = renderWindow.connection
val rateLimiter = RateLimiter()
fun init() {
renderWindow.inputHandler.registerKeyCallback("minosoft:pick_item".toResourceLocation(), KeyBinding(
mutableMapOf(
KeyAction.PRESS to mutableSetOf(KeyCodes.MOUSE_BUTTON_MIDDLE),
),
)) {
pickItem(true) // ToDo: Combination for not copying nbt
}
}
fun pickItem(copyNBT: Boolean) {
if (!connection.player.baseAbilities.creative) {
return
}
val raycast = renderWindow.inputHandler.camera.target ?: return
if (raycast.distance > connection.player.reachDistance) {
return
}
val itemStack: ItemStack?
when (raycast) {
is BlockRaycastHit -> {
itemStack = ItemStack(raycast.blockState.block.item!!, connection, 1)
if (copyNBT) {
val blockEntity = connection.world.getBlockEntity(raycast.blockPosition)
blockEntity?.nbt?.let { itemStack.nbt.putAll(it) }
}
}
is EntityRaycastHit -> {
val entity = raycast.entity
itemStack = entity.entityType.spawnEgg?.let { ItemStack(it, connection) } ?: let {
entity.equipment[InventorySlots.EquipmentSlots.MAIN_HAND]?.copy()
}
}
else -> {
itemStack = null
}
}
if (itemStack == null) {
return
}
for (i in 0 until PlayerInventory.HOTBAR_SLOTS) {
val slot = connection.player.inventory.getHotbarSlot(i) ?: continue
if (slot != itemStack) {
continue
}
interactionManager.hotbar.selectSlot(i)
return
}
val selectedSlot = connection.player.selectedHotbarSlot + PlayerInventory.HOTBAR_OFFSET
rateLimiter += { connection.sendPacket(ItemStackCreateC2SP(selectedSlot, itemStack)) }
connection.player.inventory[selectedSlot] = itemStack
}
fun draw(delta: Double) {
rateLimiter.work()
}
}

View File

@ -75,7 +75,7 @@ class AudioPlayer(
private fun preloadSounds() {
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.VERBOSE) { "Preloading sounds..." }
Log.log(LogMessageType.AUDIO_LOADING, LogLevels.VERBOSE) { "Preloading sounds..." }
if (SoundConstants.DISABLE_PRELOADING) {
return
}
@ -233,7 +233,7 @@ class AudioPlayer(
}
private fun loadSounds() {
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.VERBOSE) { "Loading sounds.json" }
Log.log(LogMessageType.AUDIO_LOADING, LogLevels.VERBOSE) { "Loading sounds.json" }
val data = connection.assetsManager.readJsonAsset(SOUNDS_INDEX_FILE)
for ((soundEventResourceLocation, json) in data) {

View File

@ -55,7 +55,7 @@ class PlayerAbilitiesS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
abilities.isInvulnerable = isInvulnerable
abilities.isFlying = isFlying
abilities.canFly = canFly
abilities.canInstantBreak = canInstantBuild
abilities.creative = canInstantBuild
abilities.flyingSpeed = flyingSpeed
abilities.walkingSpeed = walkingSpeed

View File

@ -154,18 +154,18 @@ open class OutByteBuffer(open val connection: Connection? = null) {
writeByte(type.ordinal)
}
fun writeNBT(nbt: Any?, compressed: Boolean) {
fun writeNBT(nbt: Any?, compressed: Boolean = false) {
if (compressed) {
TODO("Can not write compressed NBT yet!")
}
if (nbt is Collection<*> && nbt.isEmpty()) {
return writeNBTTag(null)
}
if (nbt is Map<*, *>) {
if (nbt.isEmpty()) {
return writeNBTTag(null)
}
writeNBTTagType(NBTTagTypes.COMPOUND)
writeShort(0) // Length of compound tag name
writeNBTTag(nbt, false)
return
}
writeNBTTag(nbt)
}
@ -203,13 +203,12 @@ open class OutByteBuffer(open val connection: Connection? = null) {
writeUnprefixedByteArray(bytes)
}
is Collection<*> -> {
val collectionType: NBTTagTypes = if (tag.isEmpty()) {
this.writeNBTTagType(if (tag.isEmpty()) {
NBTTagTypes.END
} else {
tag.iterator().next().type
}
this.writeNBTTagType(collectionType)
// write Type
})
writeInt(tag.size)
for (element in tag) {
@ -217,22 +216,16 @@ open class OutByteBuffer(open val connection: Connection? = null) {
}
}
is Map<*, *> -> {
val size = tag.size
var index = 0
for ((key, value) in tag) {
val valueType = value.type
if (valueType == NBTTagTypes.END) {
break
error("NBT does not support null as value in a compound tag!")
}
this.writeNBTTagType(valueType)
writeNBTTag(key?.toString() ?: "", false)
writeNBTTag(value, true)
index++
writeNBTTag(value, false)
}
if (index - 1 != size) {
error("NBT does not support null as value in a compound tag!")
}
writeNBTTagType(NBTTagTypes.END)
this.writeNBTTagType(NBTTagTypes.END)
}
is IntArray -> {
writeInt(tag.size)