item cooldown, wip item interaction, outsource block placing

This commit is contained in:
Bixilon 2021-05-21 21:33:18 +02:00
parent 27f376110b
commit fd9f7000bc
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
8 changed files with 116 additions and 45 deletions

View File

@ -0,0 +1,26 @@
/*
* 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.abilities
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
data class ItemCooldown(
val start: Long,
val time: Int,
) {
val end: Long = start + (time * ProtocolDefinition.TICK_TIME)
val ended: Boolean
get() = System.currentTimeMillis() >= end
}

View File

@ -20,7 +20,7 @@ enum class BlockUsages {
CONSUME,
/**
* Usage get consumed (like pressing a button, opening a door, right clicking on block entities) with animation
* Usage get consumed (like pressing a button, opening a door, right clicking on block entities, placing a block, ) with animation
*/
SUCCESS,
@ -28,10 +28,5 @@ enum class BlockUsages {
* Nothing happens from block side (e.g. right clicking on dirt). You can maybe place a block, whatever
*/
PASS,
/**
* Nothing happens, basically `CONSUME`, but a requirement was not satisfied.
*/
FAIL,
;
}

View File

@ -14,9 +14,19 @@
package de.bixilon.minosoft.data.mappings.items
import com.google.gson.JsonObject
import de.bixilon.minosoft.data.abilities.Gamemodes
import de.bixilon.minosoft.data.inventory.ItemStack
import de.bixilon.minosoft.data.mappings.ResourceLocation
import de.bixilon.minosoft.data.mappings.blocks.BlockState
import de.bixilon.minosoft.data.mappings.blocks.BlockUsages
import de.bixilon.minosoft.data.mappings.blocks.types.Block
import de.bixilon.minosoft.data.mappings.versions.Registries
import de.bixilon.minosoft.data.player.Hands
import de.bixilon.minosoft.gui.rendering.input.camera.RaycastHit
import de.bixilon.minosoft.gui.rendering.util.VecUtil.plus
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import de.bixilon.minosoft.protocol.packets.c2s.play.BlockPlaceC2SP
import glm_.vec3.Vec3i
open class BlockItem(
resourceLocation: ResourceLocation,
@ -24,4 +34,44 @@ open class BlockItem(
data: JsonObject,
) : Item(resourceLocation, registries, data) {
val block: Block = registries.blockRegistry[data["block"].asInt]
override fun use(connection: PlayConnection, blockState: BlockState, blockPosition: Vec3i, raycastHit: RaycastHit, hands: Hands, itemStack: ItemStack): BlockUsages {
if (!connection.player.entity.gamemode.canBuild) {
return BlockUsages.PASS
}
val placePosition = raycastHit.blockPosition + raycastHit.hitDirection
val dimension = connection.world.dimension!!
if (placePosition.y < dimension.minY || placePosition.y >= dimension.height) {
return BlockUsages.PASS
}
connection.world[placePosition]?.let {
if (!it.material.replaceable) {
return BlockUsages.PASS
}
}
val placeBlockState = block.getPlacementState(connection, raycastHit) ?: return BlockUsages.PASS
connection.world[placePosition] = placeBlockState
if (connection.player.entity.gamemode != Gamemodes.CREATIVE) {
itemStack.count--
connection.player.inventory.validate()
}
connection.sendPacket(BlockPlaceC2SP(
position = placePosition,
direction = raycastHit.hitDirection,
cursorPosition = raycastHit.hitPosition,
item = connection.player.inventory.getHotbarSlot(),
hand = Hands.MAIN_HAND,
insideBlock = false, // ToDo
))
return BlockUsages.SUCCESS
}
}

View File

@ -50,7 +50,7 @@ open class Item(
return resourceLocation.toString()
}
open fun use(connection: PlayConnection, blockState: BlockState, blockPosition: Vec3i, raycastHit: RaycastHit, hands: Hands, itemStack: ItemStack?): BlockUsages {
open fun use(connection: PlayConnection, blockState: BlockState, blockPosition: Vec3i, raycastHit: RaycastHit, hands: Hands, itemStack: ItemStack): BlockUsages {
return BlockUsages.PASS
}

View File

@ -12,9 +12,11 @@
*/
package de.bixilon.minosoft.data.player
import de.bixilon.minosoft.data.abilities.ItemCooldown
import de.bixilon.minosoft.data.accounts.Account
import de.bixilon.minosoft.data.entities.EntityRotation
import de.bixilon.minosoft.data.entities.entities.player.PlayerEntity
import de.bixilon.minosoft.data.mappings.items.Item
import de.bixilon.minosoft.data.mappings.other.containers.Container
import de.bixilon.minosoft.data.mappings.other.containers.PlayerInventory
import de.bixilon.minosoft.gui.rendering.util.VecUtil
@ -41,4 +43,6 @@ class Player(
val containers: MutableMap<Int, Container> = synchronizedMapOf(
ProtocolDefinition.PLAYER_INVENTORY_ID to inventory)
var selectedHotbarSlot: Int = 0
val itemCooldown: MutableMap<Item, ItemCooldown> = synchronizedMapOf()
}

View File

@ -15,16 +15,14 @@ package de.bixilon.minosoft.gui.rendering.input
import de.bixilon.minosoft.config.config.game.controls.KeyBindingsNames
import de.bixilon.minosoft.data.Directions
import de.bixilon.minosoft.data.abilities.Gamemodes
import de.bixilon.minosoft.data.mappings.blocks.BlockUsages
import de.bixilon.minosoft.data.mappings.items.BlockItem
import de.bixilon.minosoft.data.player.Hands
import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.util.VecUtil.plus
import de.bixilon.minosoft.protocol.packets.c2s.play.ArmSwingC2SP
import de.bixilon.minosoft.protocol.packets.c2s.play.BlockBreakC2SP
import de.bixilon.minosoft.protocol.packets.c2s.play.BlockPlaceC2SP
import de.bixilon.minosoft.protocol.packets.c2s.play.ItemUseC2SP
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import glm_.vec3.Vec3i
@ -104,8 +102,13 @@ class InteractionHandler(
if (raycastHit.distance > RenderConstants.MAX_BLOCK_OUTLINE_RAYCAST_DISTANCE) {
return
}
val itemInHand = connection.player.inventory.getHotbarSlot()
val usage = raycastHit.blockState.block.use(renderWindow.connection, raycastHit.blockState, raycastHit.blockPosition, raycastHit, Hands.MAIN_HAND, null) // ToDo
val usage = if (renderWindow.inputHandler.camera.sneaking) {
BlockUsages.PASS
} else {
raycastHit.blockState.block.use(renderWindow.connection, raycastHit.blockState, raycastHit.blockPosition, raycastHit, Hands.MAIN_HAND, itemInHand)
}
lastInteractionSent = currentTime
lastInteraction = currentTime
@ -128,47 +131,32 @@ class InteractionHandler(
}
BlockUsages.PASS -> {
// use item or place block
if (!connection.player.entity.gamemode.canBuild) {
return
}
val selectedItemStack = connection.player.inventory.getHotbarSlot() ?: return
val blockPosition = raycastHit.blockPosition + raycastHit.hitDirection
itemInHand ?: return
val dimension = connection.world.dimension!!
if (blockPosition.y < dimension.minY || blockPosition.y >= dimension.height) {
return
}
val cooldown = connection.player.itemCooldown[itemInHand.item]
renderWindow.connection.world[blockPosition]?.let {
if (!it.material.replaceable) {
return
}
}
val blockState = if (selectedItemStack.item is BlockItem) {
selectedItemStack.item.block.getPlacementState(renderWindow.connection, raycastHit) ?: return
cooldown?.let {
if (it.ended) {
connection.player.itemCooldown.remove(itemInHand.item)
} else {
return
}
renderWindow.connection.world[blockPosition] = blockState
if (connection.player.entity.gamemode != Gamemodes.CREATIVE) {
selectedItemStack.count--
renderWindow.connection.player.inventory.validate()
}
connection.sendPacket(ArmSwingC2SP(Hands.MAIN_HAND))
connection.sendPacket(BlockPlaceC2SP(
position = blockPosition,
direction = raycastHit.hitDirection,
cursorPosition = raycastHit.hitPosition,
item = connection.player.inventory.getHotbarSlot(),
hand = Hands.MAIN_HAND,
insideBlock = false, // ToDo
))
when (itemInHand.item.use(connection, raycastHit.blockState, raycastHit.blockPosition, raycastHit, Hands.MAIN_HAND, itemInHand)) {
BlockUsages.SUCCESS -> {
connection.sendPacket(ArmSwingC2SP(Hands.MAIN_HAND))
}
BlockUsages.PASS -> {
return
}
BlockUsages.CONSUME -> {
}
}
// ToDo: Before 1.9
connection.sendPacket(ItemUseC2SP(Hands.MAIN_HAND))
}
}

View File

@ -94,6 +94,8 @@ class Camera(
var viewProjectionMatrix = projectionMatrix * viewMatrix
private set
var sneaking: Boolean = false // ToDo: Not yet implemented
fun mouseCallback(xPos: Double, yPos: Double) {
var xOffset = xPos - this.lastMouseX

View File

@ -12,6 +12,8 @@
*/
package de.bixilon.minosoft.protocol.packets.s2c.play
import de.bixilon.minosoft.data.abilities.ItemCooldown
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
import de.bixilon.minosoft.util.logging.Log
@ -22,6 +24,10 @@ class ItemCooldownSetS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
val item = buffer.connection.registries.itemRegistry[buffer.readVarInt()]
val time = buffer.readVarInt()
override fun handle(connection: PlayConnection) {
connection.player.itemCooldown[item] = ItemCooldown(System.currentTimeMillis(), time)
}
override fun log() {
Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "Item cooldown set (item=$item, time=$time)" }
}