diff --git a/src/main/java/de/bixilon/minosoft/data/abilities/ItemCooldown.kt b/src/main/java/de/bixilon/minosoft/data/abilities/ItemCooldown.kt new file mode 100644 index 000000000..99a51cbce --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/abilities/ItemCooldown.kt @@ -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 . + * + * 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 +} diff --git a/src/main/java/de/bixilon/minosoft/data/mappings/blocks/BlockUsages.kt b/src/main/java/de/bixilon/minosoft/data/mappings/blocks/BlockUsages.kt index b2f44701a..ebeee1142 100644 --- a/src/main/java/de/bixilon/minosoft/data/mappings/blocks/BlockUsages.kt +++ b/src/main/java/de/bixilon/minosoft/data/mappings/blocks/BlockUsages.kt @@ -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, ; } diff --git a/src/main/java/de/bixilon/minosoft/data/mappings/items/BlockItem.kt b/src/main/java/de/bixilon/minosoft/data/mappings/items/BlockItem.kt index 0b1eda526..2abe9eefb 100644 --- a/src/main/java/de/bixilon/minosoft/data/mappings/items/BlockItem.kt +++ b/src/main/java/de/bixilon/minosoft/data/mappings/items/BlockItem.kt @@ -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 + } } diff --git a/src/main/java/de/bixilon/minosoft/data/mappings/items/Item.kt b/src/main/java/de/bixilon/minosoft/data/mappings/items/Item.kt index 3b086436e..e9b646cb3 100644 --- a/src/main/java/de/bixilon/minosoft/data/mappings/items/Item.kt +++ b/src/main/java/de/bixilon/minosoft/data/mappings/items/Item.kt @@ -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 } diff --git a/src/main/java/de/bixilon/minosoft/data/player/Player.kt b/src/main/java/de/bixilon/minosoft/data/player/Player.kt index 1e1f0fa03..aabd8e41c 100644 --- a/src/main/java/de/bixilon/minosoft/data/player/Player.kt +++ b/src/main/java/de/bixilon/minosoft/data/player/Player.kt @@ -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 = synchronizedMapOf( ProtocolDefinition.PLAYER_INVENTORY_ID to inventory) var selectedHotbarSlot: Int = 0 + + val itemCooldown: MutableMap = synchronizedMapOf() } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/input/InteractionHandler.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/input/InteractionHandler.kt index bcc964083..01b7ca92b 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/input/InteractionHandler.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/input/InteractionHandler.kt @@ -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) { + cooldown?.let { + if (it.ended) { + connection.player.itemCooldown.remove(itemInHand.item) + } else { return } } - val blockState = if (selectedItemStack.item is BlockItem) { - selectedItemStack.item.block.getPlacementState(renderWindow.connection, raycastHit) ?: return - } else { - return + + 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 -> { + } } - - 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 - )) + // ToDo: Before 1.9 + connection.sendPacket(ItemUseC2SP(Hands.MAIN_HAND)) } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Camera.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Camera.kt index 26a345ad6..f2ab5052e 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Camera.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Camera.kt @@ -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 @@ -328,7 +330,7 @@ class Camera( if (distance >= 0.0f && blockState != null) { currentPosition += direction * distance return RaycastHit( - currentPosition , + currentPosition, getTotalDistance() + distance, blockState = blockState, hitDirection = currentPosition.nearestIntegerPositionDirection, diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/ItemCooldownSetS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/ItemCooldownSetS2CP.kt index 2bf6048f3..3927949a2 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/ItemCooldownSetS2CP.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/ItemCooldownSetS2CP.kt @@ -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)" } }