improved item interaction, fix entity meta data receiving

This commit is contained in:
Bixilon 2021-10-31 19:42:37 +01:00
parent 35733d907f
commit 397cfd916c
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
16 changed files with 142 additions and 31 deletions

View File

@ -84,7 +84,7 @@ abstract class Entity(
protected val versionId: Int = connection.version.versionId
open var attachedEntity: Int? = null
var entityMetaData: EntityMetaData = EntityMetaData(connection)
val entityMetaData: EntityMetaData = EntityMetaData(connection)
var vehicle: Entity? = null
var passengers: MutableSet<Entity> = synchronizedSetOf()

View File

@ -0,0 +1,70 @@
/*
* 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.data.registries.items
import de.bixilon.minosoft.data.abilities.Gamemodes
import de.bixilon.minosoft.data.inventory.ItemStack
import de.bixilon.minosoft.data.player.Hands
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.registries.Registries
import de.bixilon.minosoft.gui.rendering.gui.hud.elements.hotbar.HotbarHungerElement
import de.bixilon.minosoft.gui.rendering.input.interaction.InteractionResults
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.util.KUtil.decide
import de.bixilon.minosoft.util.KUtil.toBoolean
import de.bixilon.minosoft.util.KUtil.toFloat
import de.bixilon.minosoft.util.KUtil.toInt
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.asCompound
open class FoodItem(
resourceLocation: ResourceLocation,
registries: Registries,
data: Map<String, Any>,
) : Item(resourceLocation, registries, data), UsableItem {
val nutrition: Int
val saturationModifier: Float
val isMeat: Boolean
val alwaysEdiable: Boolean
val timeToEat: Int
init {
val foodProperties = data["food_properties"].asCompound()
nutrition = foodProperties["nutrition"]?.toInt() ?: 0
saturationModifier = foodProperties["saturation_modifier"]?.toFloat() ?: 0.0f
isMeat = foodProperties["is_meat"]?.toBoolean() ?: false
alwaysEdiable = foodProperties["can_always_eat"]?.toBoolean() ?: false
timeToEat = foodProperties["time_to_eat"]?.toInt() ?: foodProperties["fast_food"]?.toBoolean()?.decide(16, 32) ?: 100
}
override val maxUseTime: Int = timeToEat
override fun interactItem(connection: PlayConnection, hand: Hands, itemStack: ItemStack): InteractionResults {
val hunger = connection.player.healthCondition.hunger
if (hunger < HotbarHungerElement.MAX_HUNGER || alwaysEdiable) {
connection.player.useItem(hand)
}
return InteractionResults.CONSUME
}
override fun finishUsing(connection: PlayConnection, itemStack: ItemStack) {
Log.log(LogMessageType.OTHER, LogLevels.VERBOSE) { "Finished eating: $timeToEat" }
if (connection.player.gamemode != Gamemodes.CREATIVE) {
itemStack.count--
}
// ToDo: Apply eating effect(s)
}
}

View File

@ -81,7 +81,7 @@ open class Item(
return InteractionResults.PASS
}
open fun interactItem(connection: PlayConnection, hand: Hands, itemStack: ItemStack, ticks: Int): InteractionResults {
open fun interactItem(connection: PlayConnection, hand: Hands, itemStack: ItemStack): InteractionResults {
return InteractionResults.PASS
}
@ -90,6 +90,9 @@ open class Item(
override fun deserialize(registries: Registries?, resourceLocation: ResourceLocation, data: Map<String, Any>): Item {
check(registries != null) { "Registries is null!" }
if (data["food_properties"] != null) {
return FoodItem(resourceLocation, registries, data)
}
return when (val `class` = data["class"].unsafeCast<String>()) {
"BlockItem" -> BlockItem(resourceLocation, registries, data)
"Item", "AirBlockItem" -> Item(resourceLocation, registries, data)

View File

@ -24,9 +24,10 @@ open class ShieldItem(
resourceLocation: ResourceLocation,
registries: Registries,
data: Map<String, Any>,
) : Item(resourceLocation, registries, data) {
) : Item(resourceLocation, registries, data), UsableItem {
override val maxUseTime: Int = Int.MAX_VALUE
override fun interactItem(connection: PlayConnection, hand: Hands, itemStack: ItemStack, ticks: Int): InteractionResults {
override fun interactItem(connection: PlayConnection, hand: Hands, itemStack: ItemStack): InteractionResults {
connection.player.useItem(hand)
return InteractionResults.CONSUME
}

View File

@ -0,0 +1,23 @@
/*
* 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.data.registries.items
import de.bixilon.minosoft.data.inventory.ItemStack
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
interface UsableItem {
val maxUseTime: Int
fun finishUsing(connection: PlayConnection, itemStack: ItemStack) {}
}

View File

@ -222,7 +222,7 @@ class HotbarHealthElement(hudRenderer: HUDRenderer) : AbstractHotbarHealthElemen
val wither = witherStatusEffect?.let { player.activeStatusEffects[it] != null } ?: false
val frozen = player.ticksFrozen > 0
val absorptionsAmount = max(0.0f, player.playerAbsorptionHearts) // ToDo: This is (probably) calculated as effect instance
val absorptionsAmount = max(0.0f, player.playerAbsorptionHearts)
val maxHealth = max(0.0f, player.getAttributeValue(DefaultStatusEffectAttributeNames.GENERIC_MAX_HEALTH).toFloat())

View File

@ -160,7 +160,7 @@ class HotbarHungerElement(hudRenderer: HUDRenderer) : Element(hudRenderer), Poll
}
companion object {
private const val MAX_HUNGER = 20
const val MAX_HUNGER = 20
private const val HUNGER_CONTAINERS = MAX_HUNGER / 2
private val HUNGER_SIZE = Vec2i(8, 9)
}

View File

@ -26,7 +26,7 @@ import glm_.vec2.Vec2i
class BreakProgressHUDElement(hudRenderer: HUDRenderer) : LayoutedHUDElement<TextElement>(hudRenderer), Drawable {
override val layout: TextElement = TextElement(hudRenderer, "")
private val leftClickHandler = hudRenderer.renderWindow.inputHandler.leftClickHandler
private val breakInteractionHandler = hudRenderer.renderWindow.inputHandler.interactionManager.`break`
override val layoutOffset: Vec2i
get() = Vec2i((hudRenderer.scaledSize.x / 2) + CrosshairHUDElement.CROSSHAIR_SIZE / 2 + 5, (hudRenderer.scaledSize.y - layout.size.y) / 2)
@ -34,13 +34,13 @@ class BreakProgressHUDElement(hudRenderer: HUDRenderer) : LayoutedHUDElement<Tex
private var percent = -1
override fun draw() {
val breakProgress = leftClickHandler.breakProgress
val breakProgress = breakInteractionHandler.breakProgress
if (breakProgress <= 0 || breakProgress >= 1.0) {
layout.text = ""
this.percent = -1
return
}
val percent = (leftClickHandler.breakProgress * 100).toInt()
val percent = (breakInteractionHandler.breakProgress * 100).toInt()
if (percent == this.percent) {
return
}

View File

@ -18,6 +18,4 @@ import de.bixilon.minosoft.gui.rendering.RenderWindow
@Deprecated("TODO")
class AttackInteractionHandler(
val renderWindow: RenderWindow,
) {
val isBreakingBlock: Boolean = false
}
)

View File

@ -11,7 +11,7 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.input
package de.bixilon.minosoft.gui.rendering.input.interaction
import de.bixilon.minosoft.config.key.KeyAction
import de.bixilon.minosoft.config.key.KeyBinding
@ -35,8 +35,7 @@ import de.bixilon.minosoft.util.KUtil.toResourceLocation
import glm_.pow
import glm_.vec3.Vec3i
@Deprecated("Will be integrated in the InteractionManager")
class LeftClickHandler(
class BreakInteractionHandler(
val renderWindow: RenderWindow,
) {
private val connection = renderWindow.connection
@ -45,7 +44,8 @@ class LeftClickHandler(
private var breakBlockState: BlockState? = null
var breakProgress = Double.NEGATIVE_INFINITY
private set
val breakingBlock: Boolean
get() = breakPosition != null
private var breakSelectedSlot: Int = -1
private var breakItemInHand: ItemStack? = null

View File

@ -19,6 +19,7 @@ import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.data.abilities.Gamemodes
import de.bixilon.minosoft.data.inventory.ItemStack
import de.bixilon.minosoft.data.player.Hands
import de.bixilon.minosoft.data.registries.items.UsableItem
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
@ -41,7 +42,7 @@ class InteractInteractionHandler(
private var interactingItem: ItemStack? = null
private var interactingSlot: Int = -1
private var interactingTicks = 0
private var interactingTicksLeft = 0
private var previousDown = false
private var autoInteractionDelay = 0
@ -66,7 +67,7 @@ class InteractInteractionHandler(
connection.sendPacket(PlayerActionC2SP(PlayerActionC2SP.Actions.RELEASE_ITEM))
interactingItem = null
interactingSlot = -1
interactingTicks = 0
interactingTicksLeft = 0
}
fun interactBlock(hit: BlockRaycastHit, item: ItemStack?, hand: Hands): InteractionResults {
@ -118,7 +119,7 @@ class InteractInteractionHandler(
return InteractionResults.PASS
}
fun interactItem(item: ItemStack, hand: Hands, ticks: Int): InteractionResults {
fun interactItem(item: ItemStack, hand: Hands): InteractionResults {
if (connection.player.gamemode == Gamemodes.SPECTATOR) {
return InteractionResults.SUCCESS
}
@ -133,11 +134,11 @@ class InteractInteractionHandler(
}
return item.item.interactItem(connection, hand, item, ticks)
return item.item.interactItem(connection, hand, item)
}
fun useItem() {
if (interactionManager.attack.isBreakingBlock) {
if (interactionManager.`break`.breakingBlock) {
return
}
@ -180,14 +181,19 @@ class InteractInteractionHandler(
if (item != interactingItem || interactingSlot != selectedSlot) {
interactingItem = item
interactingSlot = selectedSlot
interactingTicks = 0
val itemType = item?.item
interactingTicksLeft = if (itemType is UsableItem) {
itemType.maxUseTime
} else {
0
}
}
if (item == null) {
continue
}
val result = interactItem(item, hand, interactingTicks++)
val result = interactItem(item, hand)
if (result == InteractionResults.SUCCESS) {
interactionManager.swingHand(hand)
@ -209,11 +215,22 @@ class InteractInteractionHandler(
val keyDown = renderWindow.inputHandler.isKeyBindingDown(USE_ITEM_KEYBINDING)
if (keyDown) {
autoInteractionDelay++
val interactingItem = interactingItem
val item = interactingItem?.item
if (item is UsableItem) {
interactingTicksLeft--
if (interactingTicksLeft < 0) {
item.finishUsing(connection, interactingItem)
stopUsingItem()
}
}
} else {
interactingTicksLeft = 0
autoInteractionDelay = 0
stopUsingItem()
}
if (keyDown && (!previousDown || autoInteractionDelay >= 5)) {
if (keyDown && (!previousDown || (autoInteractionDelay >= 5 && interactingTicksLeft <= 0))) {
useItem()
autoInteractionDelay = 0
}

View File

@ -26,6 +26,7 @@ class InteractionManager(
val hotbar = HotbarInteractionHandler(renderWindow)
val pick = ItemPickInteractionHandler(renderWindow, this)
val attack = AttackInteractionHandler(renderWindow)
val `break` = BreakInteractionHandler(renderWindow)
val use = InteractInteractionHandler(renderWindow, this)
private val swingArmRateLimiter = RateLimiter()
@ -35,6 +36,7 @@ class InteractionManager(
fun init() {
hotbar.init()
pick.init()
`break`.init()
use.init()
}
@ -42,6 +44,7 @@ class InteractionManager(
hotbar.draw(delta)
pick.draw(delta)
// attack.draw(delta)
`break`.draw(delta)
use.draw(delta)
swingArmRateLimiter.work()

View File

@ -20,7 +20,6 @@ import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.input.LeftClickHandler
import de.bixilon.minosoft.gui.rendering.input.camera.Camera
import de.bixilon.minosoft.gui.rendering.input.interaction.InteractionManager
import de.bixilon.minosoft.gui.rendering.modding.events.input.MouseMoveEvent
@ -47,7 +46,6 @@ class RenderWindowInputHandler(
private var skipNextCharPress = false
val interactionManager = InteractionManager(renderWindow)
val leftClickHandler = LeftClickHandler(renderWindow)
init {
registerKeyCallback("minosoft:debug_mouse_catch".toResourceLocation(), KeyBinding(
@ -62,7 +60,6 @@ class RenderWindowInputHandler(
}
fun init() {
leftClickHandler.init()
interactionManager.init()
connection.registerEvent(CallbackEventInvoker.of<RawCharInputEvent> { charInput(it.char) })
@ -269,7 +266,6 @@ class RenderWindowInputHandler(
fun draw(delta: Double) {
camera.draw()
leftClickHandler.draw(delta)
interactionManager.draw(delta)
}

View File

@ -29,7 +29,7 @@ class EntityMetadataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
override fun handle(connection: PlayConnection) {
val entity = connection.world.entities[entityId] ?: return
entity.entityMetaData = metaData
entity.entityMetaData.sets.putAll(metaData.sets)
connection.fireEvent(EntityMetaDataChangeEvent(connection, this))
}

View File

@ -59,7 +59,7 @@ class MobSpawnS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
entity = entityType.build(buffer.connection, position, rotation, metaData, buffer.versionId)!!
entity.velocity = velocity
metaData?.let {
entity.entityMetaData = it
entity.entityMetaData.sets.putAll(it.sets)
if (RunConfiguration.VERBOSE_ENTITY_META_DATA_LOGGING) {
Log.log(LogMessageType.OTHER, LogLevels.VERBOSE) { "Entity meta data(entityId=$entityId): ${entity.entityMetaDataAsString}" }
}

View File

@ -77,7 +77,7 @@ class PlayerEntitySpawnS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
)
if (metaData != null) {
entity.entityMetaData = metaData
entity.entityMetaData.sets.putAll(metaData.sets)
if (RunConfiguration.VERBOSE_ENTITY_META_DATA_LOGGING) {
Log.log(LogMessageType.OTHER, level = LogLevels.VERBOSE) { "Players metadata of $entity: ${entity.entityMetaData}" }
}