improve block breaking code, wip digging

This commit is contained in:
Bixilon 2021-05-22 17:33:38 +02:00
parent 25bef60253
commit fb6e465ece
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
12 changed files with 273 additions and 142 deletions

View File

@ -38,6 +38,7 @@ open class Block(
mappings: Registries,
data: JsonObject,
) : RegistryItem {
open val hardness: Float = data["hardness"]?.asFloat ?: 0.0f
open val explosionResistance: Float = data["explosion_resistance"]?.asFloat ?: 0.0f
open val tintColor: RGBColor? = data["tint_color"]?.asInt?.let { TintColorCalculator.getJsonColor(it) }
open val randomOffsetType: RandomOffsetTypes? = data["offset_type"]?.asString?.let { RandomOffsetTypes[it] }

View File

@ -47,11 +47,17 @@ open class Item(
return resourceLocation.toString()
}
open fun getMiningSpeedMultiplier(connection: PlayConnection, blockState: BlockState, blockPosition: Vec3i, itemStack: ItemStack): Float {
return 1.0f
}
open fun use(connection: PlayConnection, blockState: BlockState, blockPosition: Vec3i, raycastHit: RaycastHit, hands: Hands, itemStack: ItemStack): BlockUsages {
return BlockUsages.PASS
}
companion object : ResourceLocationDeserializer<Item> {
const val INFINITE_MINING_SPEED_MULTIPLIER = -1.0f
override fun deserialize(mappings: Registries?, resourceLocation: ResourceLocation, data: JsonObject): Item {
check(mappings != null) { "Registries is null!" }
return when (data["class"].asString) {

View File

@ -14,9 +14,13 @@
package de.bixilon.minosoft.data.mappings.items.tools
import com.google.gson.JsonObject
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.types.Block
import de.bixilon.minosoft.data.mappings.versions.Registries
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import glm_.vec3.Vec3i
open class MiningToolItem(
resourceLocation: ResourceLocation,
@ -33,4 +37,13 @@ open class MiningToolItem(
override val attackDamage: Float = data["attack_damage"]?.asFloat ?: 1.0f
val miningSpeed: Float = data["mining_speed"]?.asFloat ?: 1.0f
override fun getMiningSpeedMultiplier(connection: PlayConnection, blockState: BlockState, blockPosition: Vec3i, itemStack: ItemStack): Float {
// ToDo: Calculate correct, Tags (21w19a)
if (diggableBlocks?.contains(blockState.block) == true) {
return 10.0f * miningSpeed
}
return super.getMiningSpeedMultiplier(connection, blockState, blockPosition, itemStack)
}
}

View File

@ -14,8 +14,13 @@
package de.bixilon.minosoft.data.mappings.items.tools
import com.google.gson.JsonObject
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.versions.Registries
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import de.bixilon.minosoft.util.KUtil.asResourceLocation
import glm_.vec3.Vec3i
open class SwordItem(
@ -24,4 +29,16 @@ open class SwordItem(
data: JsonObject,
) : ToolItem(resourceLocation, registries, data) {
override val attackDamage = data["attack_damage"]?.asFloat ?: -1.0f
override fun getMiningSpeedMultiplier(connection: PlayConnection, blockState: BlockState, blockPosition: Vec3i, itemStack: ItemStack): Float {
if (blockState.block.resourceLocation == COBWEB_BLOCK) {
return 15.0f
}
return super.getMiningSpeedMultiplier(connection, blockState, blockPosition, itemStack)
}
companion object {
val COBWEB_BLOCK = "minecraft:cobweb".asResourceLocation()
}
}

View File

@ -17,7 +17,7 @@ data class Abilities(
var isInvulnerable: Boolean = false,
var isFlying: Boolean = false,
var canFly: Boolean = false,
var canInstantBuild: Boolean = false,
var canInstantBreak: Boolean = false,
var flyingSpeed: Float = 1.0f,
var walkingSpeed: Float = 1.0f,

View File

@ -64,9 +64,9 @@ class RenderWindow(
val inputHandler = RenderWindowInputHandler(this)
var windowId = 0L
private var deltaFrameTime = 0.0 // time between current frame and last frame
private var deltaFrameTime = 0L
private var lastFrame = 0.0
private var lastFrame = 0L
private val latch = CountUpAndDownLatch(1)
private var renderingState = RenderingStates.RUNNING
@ -312,7 +312,7 @@ class RenderWindow(
this.lastTickTimer = currentTickTime
}
val currentFrame = glfwGetTime()
val currentFrame = System.currentTimeMillis()
deltaFrameTime = currentFrame - lastFrame
lastFrame = currentFrame
@ -329,8 +329,7 @@ class RenderWindow(
glfwSwapBuffers(windowId)
glfwPollEvents()
inputHandler.draw()
inputHandler.camera.handleInput(deltaFrameTime)
inputHandler.draw(deltaFrameTime)
// handle opengl context tasks, but limit it per frame
var actionsDone = 0

View File

@ -0,0 +1,144 @@
/*
* 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
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.inventory.ItemStack
import de.bixilon.minosoft.data.mappings.blocks.BlockState
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.protocol.packets.c2s.play.ArmSwingC2SP
import de.bixilon.minosoft.protocol.packets.c2s.play.BlockBreakC2SP
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import glm_.vec3.Vec3i
class LeftClickHandler(
val renderWindow: RenderWindow,
) {
private val connection = renderWindow.connection
private var breakPosition: Vec3i? = null
private var breakBlockState: BlockState? = null
private var breakPercentBroken: Float = -1.0f
private var breakSelectedSlot: Int = -1
private var breakItemInHand: ItemStack? = null
private var breakSent = 0L
private var creativeLastHoldBreakTime = 0L
private fun clearDigging() {
breakPosition = null
breakBlockState = null
breakPercentBroken = -1.0f
breakSelectedSlot = -1
breakItemInHand = null
}
private fun cancelDigging() {
breakPosition?.let {
connection.sendPacket(BlockBreakC2SP(BlockBreakC2SP.BreakType.CANCELLED_DIGGING, breakPosition, Directions.UP)) // ToDo: Direction?
clearDigging()
}
}
private fun checkBreaking(isKeyDown: Boolean, deltaTime: Long) {
val currentTime = System.currentTimeMillis()
if (!isKeyDown) {
creativeLastHoldBreakTime = 0L
cancelDigging()
return
}
if (!connection.player.entity.gamemode.canBreak) {
cancelDigging()
return
}
val raycastHit = renderWindow.inputHandler.camera.getTargetBlock()
if (raycastHit == null) {
cancelDigging()
return
}
if (raycastHit.distance >= RenderConstants.MAX_BLOCK_OUTLINE_RAYCAST_DISTANCE) {
cancelDigging()
return
}
// check if we look at another block or our inventory changed
if (breakPosition != raycastHit.blockPosition || breakBlockState != raycastHit.blockState || breakSelectedSlot != connection.player.selectedHotbarSlot || breakItemInHand !== connection.player.inventory.getHotbarSlot()) {
cancelDigging()
}
fun startDigging() {
if (breakPosition != null) {
return
}
connection.sendPacket(BlockBreakC2SP(BlockBreakC2SP.BreakType.START_DIGGING, raycastHit.blockPosition, raycastHit.hitDirection))
breakPosition = raycastHit.blockPosition
breakBlockState = raycastHit.blockState
breakPercentBroken = 0.0f
breakSelectedSlot = connection.player.selectedHotbarSlot
breakItemInHand = connection.player.inventory.getHotbarSlot()
}
fun finishDigging() {
connection.sendPacket(BlockBreakC2SP(BlockBreakC2SP.BreakType.FINISHED_DIGGING, raycastHit.blockPosition, raycastHit.hitDirection))
clearDigging()
connection.world.setBlockState(raycastHit.blockPosition, null)
}
if (currentTime - breakSent <= ProtocolDefinition.TICK_TIME) {
return
}
breakSent = currentTime
val canInstantBreak = connection.player.baseAbilities.canInstantBreak || connection.player.entity.gamemode == Gamemodes.CREATIVE
if (canInstantBreak) {
// creative
if (currentTime - creativeLastHoldBreakTime <= ProtocolDefinition.TICK_TIME * 5) {
return
}
connection.sendPacket(ArmSwingC2SP(Hands.MAIN_HAND))
startDigging()
finishDigging()
creativeLastHoldBreakTime = currentTime
return
}
startDigging()
connection.sendPacket(ArmSwingC2SP(Hands.MAIN_HAND))
}
fun init() {
renderWindow.inputHandler.registerCheckCallback(KeyBindingsNames.DESTROY_BLOCK)
}
fun draw(deltaTime: Long) {
// ToDo: Entity attacking, arm swing animation when hitting air
checkBreaking(renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.DESTROY_BLOCK), deltaTime)
}
}

View File

@ -14,77 +14,24 @@
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.mappings.blocks.BlockUsages
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.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
class InteractionHandler(
class RightClickHandler(
val renderWindow: RenderWindow,
) {
private val connection = renderWindow.connection
private var lastInteraction = 0L
private var lastInteractionSent = 0L
private var lastBreak = 0L
private var lastBreakSent = 0L
private var currentlyBreakingBlock: Vec3i? = null
fun init() {
renderWindow.inputHandler.registerCheckCallback(KeyBindingsNames.BLOCK_INTERACT)
renderWindow.inputHandler.registerCheckCallback(KeyBindingsNames.DESTROY_BLOCK)
}
private fun checkBreaking(isKeyDown: Boolean) {
val currentTime = System.currentTimeMillis()
if (!isKeyDown) {
lastBreak = 0L
return
}
if (currentTime - lastBreak < ProtocolDefinition.TICK_TIME * 5) {
return
}
val raycastHit = renderWindow.inputHandler.camera.getTargetBlock()
fun cancel() {
currentlyBreakingBlock?.let {
connection.sendPacket(BlockBreakC2SP(BlockBreakC2SP.BreakType.CANCELLED_DIGGING, currentlyBreakingBlock, Directions.UP)) // ToDo
currentlyBreakingBlock = null
}
}
if (raycastHit?.blockPosition != currentlyBreakingBlock) {
cancel()
}
if ((raycastHit?.distance ?: Float.MAX_VALUE) > RenderConstants.MAX_BLOCK_OUTLINE_RAYCAST_DISTANCE) {
cancel()
return
}
raycastHit ?: return
if (currentTime - lastBreakSent < ProtocolDefinition.TICK_TIME) {
return
}
if (!connection.player.entity.gamemode.canBreak) {
return
}
currentlyBreakingBlock = raycastHit.blockPosition
connection.sendPacket(BlockBreakC2SP(BlockBreakC2SP.BreakType.START_DIGGING, raycastHit.blockPosition, raycastHit.hitDirection))
connection.sendPacket(BlockBreakC2SP(BlockBreakC2SP.BreakType.FINISHED_DIGGING, raycastHit.blockPosition, raycastHit.hitDirection))
connection.world.setBlockState(raycastHit.blockPosition, null)
currentlyBreakingBlock = null
lastBreak = currentTime
lastBreakSent = currentTime
}
private fun checkInteraction(isKeyDown: Boolean) {
@ -170,8 +117,8 @@ class InteractionHandler(
}
fun draw() {
fun draw(deltaTime: Long) {
// ToDo: Entity interaction, shield/sword blocking, etc
checkInteraction(renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.BLOCK_INTERACT))
checkBreaking(renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.DESTROY_BLOCK))
}
}

View File

@ -136,74 +136,6 @@ class Camera(
connection.fireEvent(FrustumChangeEvent(renderWindow, frustum))
}
fun handleInput(deltaTime: Double) {
if (renderWindow.inputHandler.currentKeyConsumer != null) { // ToDo
return
}
var cameraSpeed = if (connection.player.entity.isFlying) {
flyingSpeed
} else {
walkingSpeed
} * deltaTime
val movementFront = Vec3(cameraFront)
if (!Minosoft.getConfig().config.game.camera.noCipMovement) {
movementFront.y = 0.0f
movementFront.normalizeAssign() // when moving forwards, do not move down
}
if (renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.MOVE_SPRINT)) {
cameraSpeed *= PLAYER_SPRINT_SPEED_MODIFIER
}
if (ProtocolDefinition.FAST_MOVEMENT) {
cameraSpeed *= 5
}
val movementDirection = Vec3()
if (renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.MOVE_FORWARD)) {
movementDirection += movementFront
}
if (renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.MOVE_BACKWARDS)) {
movementDirection -= movementFront
}
if (renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.MOVE_LEFT)) {
movementDirection -= cameraRight
}
if (renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.MOVE_RIGHT)) {
movementDirection += cameraRight
}
val deltaMovement = if (movementDirection != VecUtil.EMPTY_VEC3) {
movementDirection.normalize() * cameraSpeed
} else {
movementDirection
}
if (playerEntity.isFlying) {
if (renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.MOVE_FLY_UP)) {
deltaMovement += CAMERA_UP_VEC3 * cameraSpeed
}
if (renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.MOVE_FLY_DOWN)) {
deltaMovement -= CAMERA_UP_VEC3 * cameraSpeed
}
} else if (playerEntity.onGround && renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.MOVE_JUMP)) {
// TODO: jump delay, correct jump height
playerEntity.velocity.y += 0.75f * ProtocolDefinition.GRAVITY
playerEntity.onGround = false
}
if (deltaMovement != VecUtil.EMPTY_VEC3) {
playerEntity.move(deltaMovement, false)
recalculateViewProjectionMatrix()
currentPositionSent = false
sendPositionToServer()
}
val lastZoom = zoom
zoom = if (renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.ZOOM)) {
2f
} else {
0.0f
}
if (lastZoom != zoom) {
recalculateViewProjectionMatrix()
}
}
private fun recalculateViewProjectionMatrix() {
viewMatrix = calculateViewMatrix()
projectionMatrix = calculateProjectionMatrix(renderWindow.screenDimensionsF)
@ -279,11 +211,77 @@ class Camera(
sendPositionToServer()
}
fun draw() {
fun draw(deltaTime: Long) {
if (!currentPositionSent || !currentRotationSent) {
recalculateViewProjectionMatrix()
sendPositionToServer()
}
if (renderWindow.inputHandler.currentKeyConsumer != null) { // ToDo
return
}
var cameraSpeed = if (connection.player.entity.isFlying) {
flyingSpeed
} else {
walkingSpeed
} * (deltaTime / 1000.0)
val movementFront = Vec3(cameraFront)
if (!Minosoft.getConfig().config.game.camera.noCipMovement) {
movementFront.y = 0.0f
movementFront.normalizeAssign() // when moving forwards, do not move down
}
if (renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.MOVE_SPRINT)) {
cameraSpeed *= PLAYER_SPRINT_SPEED_MODIFIER
}
if (ProtocolDefinition.FAST_MOVEMENT) {
cameraSpeed *= 5
}
val movementDirection = Vec3()
if (renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.MOVE_FORWARD)) {
movementDirection += movementFront
}
if (renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.MOVE_BACKWARDS)) {
movementDirection -= movementFront
}
if (renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.MOVE_LEFT)) {
movementDirection -= cameraRight
}
if (renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.MOVE_RIGHT)) {
movementDirection += cameraRight
}
val deltaMovement = if (movementDirection != VecUtil.EMPTY_VEC3) {
movementDirection.normalize() * cameraSpeed
} else {
movementDirection
}
if (playerEntity.isFlying) {
if (renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.MOVE_FLY_UP)) {
deltaMovement += CAMERA_UP_VEC3 * cameraSpeed
}
if (renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.MOVE_FLY_DOWN)) {
deltaMovement -= CAMERA_UP_VEC3 * cameraSpeed
}
} else if (playerEntity.onGround && renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.MOVE_JUMP)) {
// TODO: jump delay, correct jump height
playerEntity.velocity.y += 0.75f * ProtocolDefinition.GRAVITY
playerEntity.onGround = false
}
if (deltaMovement != VecUtil.EMPTY_VEC3) {
playerEntity.move(deltaMovement, false)
recalculateViewProjectionMatrix()
currentPositionSent = false
sendPositionToServer()
}
val lastZoom = zoom
zoom = if (renderWindow.inputHandler.isKeyBindingDown(KeyBindingsNames.ZOOM)) {
2f
} else {
0.0f
}
if (lastZoom != zoom) {
recalculateViewProjectionMatrix()
}
}
private fun sendPositionToServer() {

View File

@ -34,12 +34,14 @@ class Frustum(private val camera: Camera) {
}
fun recalculate() {
synchronized(normals) {
normals.clear()
normals.add(camera.cameraFront.normalize())
calculateSideNormals()
calculateVerticalNormals()
}
}
private fun calculateSideNormals() {
val cameraRealUp = (camera.cameraRight cross camera.cameraFront).normalize()

View File

@ -21,7 +21,8 @@ import de.bixilon.minosoft.data.mappings.ResourceLocation
import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.hud.elements.input.KeyConsumer
import de.bixilon.minosoft.gui.rendering.input.InteractionHandler
import de.bixilon.minosoft.gui.rendering.input.LeftClickHandler
import de.bixilon.minosoft.gui.rendering.input.RightClickHandler
import de.bixilon.minosoft.gui.rendering.input.camera.Camera
import de.bixilon.minosoft.protocol.network.connection.PlayConnection
import de.bixilon.minosoft.util.logging.Log
@ -42,7 +43,8 @@ class RenderWindowInputHandler(
private var skipNextCharPress = false
private val interactionHandler = InteractionHandler(renderWindow)
private val rightClickHandler = RightClickHandler(renderWindow)
private val leftClickHandler = LeftClickHandler(renderWindow)
init {
registerKeyCallback(KeyBindingsNames.DEBUG_MOUSE_CATCH) {
@ -57,7 +59,8 @@ class RenderWindowInputHandler(
}
fun init() {
interactionHandler.init()
rightClickHandler.init()
leftClickHandler.init()
}
@ -273,8 +276,9 @@ class RenderWindowInputHandler(
return false
}
fun draw() {
camera.draw()
interactionHandler.draw()
fun draw(delta: Long) {
camera.draw(delta)
leftClickHandler.draw(delta)
rightClickHandler.draw(delta)
}
}

View File

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