prevent crash when entity position is out of bounds (of block position)

This is not done well, clamping the position will introduce weird bugs in full height dimensions, because minosoft assumes that the position is still in the world.
This commit is contained in:
Moritz Zwerger 2025-04-11 14:42:04 +02:00
parent f96047dbfe
commit 941b8c9f9c
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
10 changed files with 46 additions and 17 deletions

View File

@ -90,9 +90,9 @@ enum class Directions(
@Deprecated("outsource") @Deprecated("outsource")
fun byDirection(direction: Vec3): Directions { fun byDirection(direction: Vec3): Directions {
var minDirection = VALUES[0] var minDirection = VALUES[0]
var minError = 2.0f var minError = 2.0f * 2.0f
for (testDirection in VALUES) { for (testDirection in VALUES) {
val error = (testDirection.vectorf - direction).length() val error = (testDirection.vectorf - direction).length2()
if (error < MIN_ERROR) { if (error < MIN_ERROR) {
return testDirection return testDirection
} else if (error < minError) { } else if (error < minError) {

View File

@ -204,8 +204,6 @@ abstract class Entity(
preTick() preTick()
tick() tick()
postTick() postTick()
} catch (error: Throwable) {
error.printStackTrace()
} finally { } finally {
lastTickTime = time lastTickTime = time
lock.unlock() lock.unlock()

View File

@ -28,6 +28,7 @@ import de.bixilon.minosoft.data.world.positions.InSectionPosition
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.EMPTY import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.EMPTY
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.ONE import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.ONE
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.toVec3 import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.toVec3
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.clampBlockPosition
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.get import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.get
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.max import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.max
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.min import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.min
@ -100,7 +101,7 @@ class AABB {
} }
fun positions(order: AABBIterator.IterationOrder = AABBIterator.IterationOrder.OPTIMIZED): AABBIterator { fun positions(order: AABBIterator.IterationOrder = AABBIterator.IterationOrder.OPTIMIZED): AABBIterator {
return AABBIterator(this, order) return AABBIterator(min.clampBlockPosition(), max.clampBlockPosition(), order)
} }
fun extend(vec3: Vec3d): AABB { fun extend(vec3: Vec3d): AABB {

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.data.registries.shapes.aabb package de.bixilon.minosoft.data.registries.shapes.aabb
import de.bixilon.kotlinglm.vec3.Vec3d
import de.bixilon.minosoft.data.world.World import de.bixilon.minosoft.data.world.World
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.iterator.WorldIterator import de.bixilon.minosoft.data.world.iterator.WorldIterator
@ -30,8 +31,7 @@ class AABBIterator(
val size = maxOf(0, max.x - min.x + 1) * maxOf(0, max.y - min.y + 1) * maxOf(0, max.z - min.z + 1) val size = maxOf(0, max.x - min.x + 1) * maxOf(0, max.y - min.y + 1) * maxOf(0, max.z - min.z + 1)
constructor(aabb: AABB) : this(aabb.min.floor, aabb.max.ceil - 1) constructor(min: Vec3d, max: Vec3d, order: IterationOrder) : this(min.floor, max.ceil - 1, order)
constructor(aabb: AABB, order: IterationOrder) : this(aabb.min.floor, aabb.max.ceil - 1, order)
constructor(minX: Int, minY: Int, minZ: Int, maxX: Int, maxY: Int, maxZ: Int) : this(BlockPosition(minX, minY, minZ), BlockPosition(maxX, maxY, maxZ)) constructor(minX: Int, minY: Int, minZ: Int, maxX: Int, maxY: Int, maxZ: Int) : this(BlockPosition(minX, minY, minZ), BlockPosition(maxX, maxY, maxZ))
override fun hasNext(): Boolean { override fun hasNext(): Boolean {

View File

@ -16,12 +16,13 @@ package de.bixilon.minosoft.data.world.vec
import de.bixilon.minosoft.data.Axes import de.bixilon.minosoft.data.Axes
import de.bixilon.minosoft.data.direction.DirectionVector import de.bixilon.minosoft.data.direction.DirectionVector
import de.bixilon.minosoft.data.direction.Directions import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.text.BaseComponent
import de.bixilon.minosoft.data.text.formatting.TextFormattable
import de.bixilon.minosoft.data.world.vec.VecUtil.assertVec import de.bixilon.minosoft.data.world.vec.VecUtil.assertVec
import de.bixilon.minosoft.util.KUtil.format import de.bixilon.minosoft.util.KUtil.format
@JvmInline @JvmInline
value class SVec3(val raw: Int) { value class SVec3(val raw: Int) : TextFormattable {
constructor() : this(0, 0, 0) constructor() : this(0, 0, 0)
constructor(x: Int, y: Int, z: Int) : this(((y and MASK_Y) shl SHIFT_Y) or ((z and MASK_Z) shl SHIFT_Z) or ((x and MASK_X) shl SHIFT_X)) { constructor(x: Int, y: Int, z: Int) : this(((y and MASK_Y) shl SHIFT_Y) or ((z and MASK_Z) shl SHIFT_Z) or ((x and MASK_X) shl SHIFT_X)) {
@ -103,7 +104,16 @@ value class SVec3(val raw: Int) {
Axes.Z -> z Axes.Z -> z
} }
override fun toString() = "(${this.x.format()} ${this.y.format()} ${this.z.format()})" override fun toString() = "(${this.x} ${this.y} ${this.z})"
override fun toText() = BaseComponent().apply {
this += "("
this += x.format()
this += " "
this += y.format()
this += " "
this += z.format()
this += ")"
}
companion object { companion object {

View File

@ -47,7 +47,11 @@ class Camera(
fun draw() { fun draw() {
val entity = context.session.camera.entity val entity = context.session.camera.entity
try {
(entity.attachment.getRootVehicle() ?: entity).tryTick() // TODO (entity.attachment.getRootVehicle() ?: entity).tryTick() // TODO
} catch (error: Throwable) {
error.printStackTrace()
}
if (entity is LocalPlayerEntity) { if (entity is LocalPlayerEntity) {
entity._draw(millis()) entity._draw(millis())
} }

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.gui.rendering.util.vec.vec3 package de.bixilon.minosoft.gui.rendering.util.vec.vec3
import de.bixilon.kotlinglm.func.common.clamp
import de.bixilon.kotlinglm.vec3.Vec3 import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.kotlinglm.vec3.Vec3d import de.bixilon.kotlinglm.vec3.Vec3d
import de.bixilon.kotlinglm.vec3.Vec3i import de.bixilon.kotlinglm.vec3.Vec3i
@ -59,6 +60,19 @@ object Vec3dUtil {
return Vec3(floatArrayOf(array[0].toFloat(), array[1].toFloat(), array[2].toFloat())) return Vec3(floatArrayOf(array[0].toFloat(), array[1].toFloat(), array[2].toFloat()))
} }
private fun Double.clamp(min: Int, max: Int) = clamp(min.toDouble(), max.toDouble())
fun Vec3d.clampBlockPosition(): Vec3d { // TODO: remove +1/-1. AABBIterator otherwise crashes when subtracting one
val x = x.clamp(-BlockPosition.MAX_X + 1, BlockPosition.MAX_X - 1)
val y = y.clamp(+BlockPosition.MIN_Y + 1, BlockPosition.MAX_Y - 1)
val z = z.clamp(-BlockPosition.MAX_Z + 1, BlockPosition.MAX_Z - 1)
if (x != this.x || y != this.y || this.z != z) {
return Vec3d(x, y, z)
}
return this
}
fun interpolateLinear(delta: Double, start: Vec3d, end: Vec3d): Vec3d { fun interpolateLinear(delta: Double, start: Vec3d, end: Vec3d): Vec3d {
if (delta <= 0.0) { if (delta <= 0.0) {

View File

@ -14,6 +14,7 @@
package de.bixilon.minosoft.physics.entities package de.bixilon.minosoft.physics.entities
import de.bixilon.kotlinglm.vec3.Vec3d import de.bixilon.kotlinglm.vec3.Vec3d
import de.bixilon.kutil.math.simple.IntMath.clamp
import de.bixilon.kutil.primitive.DoubleUtil import de.bixilon.kutil.primitive.DoubleUtil
import de.bixilon.minosoft.data.direction.Directions import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.entities.EntityRotation import de.bixilon.minosoft.data.entities.EntityRotation
@ -104,10 +105,10 @@ open class EntityPhysics<E : Entity>(val entity: E) : BasicPhysicsEntity(), Abst
fun getLandingPosition(): BlockPosition { fun getLandingPosition(): BlockPosition {
val info = this.positionInfo val info = this.positionInfo
val position = BlockPosition(info.position.x, (position.y - 0.2).toInt(), info.position.z) // TODO: can y get below 0? val position = BlockPosition(info.position.x, (position.y - 0.2).toInt().clamp(BlockPosition.MIN_Y, BlockPosition.MAX_Y), info.position.z) // TODO: can y get below 0?
val inChunk = position.inChunkPosition val inChunk = position.inChunkPosition
val state = positionInfo.chunk?.get(inChunk) val state = positionInfo.chunk?.get(inChunk)
if (state == null) { if (state == null && inChunk.y > BlockPosition.MIN_Y) {
val down = positionInfo.chunk?.get(inChunk + Directions.DOWN) val down = positionInfo.chunk?.get(inChunk + Directions.DOWN)
// TODO: check if block is fence, fence gate or wall // TODO: check if block is fence, fence gate or wall
// return down // return down

View File

@ -16,11 +16,11 @@ package de.bixilon.minosoft.physics.parts
import de.bixilon.kotlinglm.vec2.Vec2d import de.bixilon.kotlinglm.vec2.Vec2d
import de.bixilon.kotlinglm.vec3.Vec3d import de.bixilon.kotlinglm.vec3.Vec3d
import de.bixilon.kutil.math.simple.DoubleMath.floor import de.bixilon.kutil.math.simple.DoubleMath.floor
import de.bixilon.kutil.math.simple.IntMath.clamp
import de.bixilon.minosoft.data.Axes import de.bixilon.minosoft.data.Axes
import de.bixilon.minosoft.data.abilities.Gamemodes import de.bixilon.minosoft.data.abilities.Gamemodes
import de.bixilon.minosoft.data.direction.Directions import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.world.positions.BlockPosition import de.bixilon.minosoft.data.world.positions.BlockPosition
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3iUtil.get
import de.bixilon.minosoft.physics.entities.living.player.local.LocalPlayerPhysics import de.bixilon.minosoft.physics.entities.living.player.local.LocalPlayerPhysics
object OutOfBlockPusher { object OutOfBlockPusher {
@ -31,7 +31,7 @@ object OutOfBlockPusher {
} }
private fun LocalPlayerPhysics.pushOutOfBlocks(x: Double, z: Double) { private fun LocalPlayerPhysics.pushOutOfBlocks(x: Double, z: Double) {
val position = BlockPosition(x.floor, this.position.y.floor, z.floor) val position = BlockPosition(x.floor, this.position.y.floor.clamp(BlockPosition.MIN_Y, BlockPosition.MAX_Y), z.floor)
if (!wouldCollidePushable(position)) { if (!wouldCollidePushable(position)) {
return return
} }

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.physics.submersion package de.bixilon.minosoft.physics.submersion
import de.bixilon.kotlinglm.func.common.clamp
import de.bixilon.kotlinglm.vec3.Vec3d import de.bixilon.kotlinglm.vec3.Vec3d
import de.bixilon.kutil.math.simple.DoubleMath.floor import de.bixilon.kutil.math.simple.DoubleMath.floor
import de.bixilon.minosoft.data.Tickable import de.bixilon.minosoft.data.Tickable
@ -67,7 +68,7 @@ class SubmersionState(private val physics: EntityPhysics<*>) : Tickable {
val totalVelocity = Vec3d.EMPTY val totalVelocity = Vec3d.EMPTY
var count = 0 var count = 0
for ((position, state, chunk) in WorldIterator(aabb.positions(), world, physics.positionInfo.chunk)) { for ((position, state, chunk) in WorldIterator(aabb, world, physics.positionInfo.chunk)) {
if (!fluid.matches(state)) continue // TODO: tags? if (!fluid.matches(state)) continue // TODO: tags?
val height = position.y + if (fluid.matches(chunk[position.inChunkPosition + Directions.UP])) 1.0f else fluid.getHeight(state) val height = position.y + if (fluid.matches(chunk[position.inChunkPosition + Directions.UP])) 1.0f else fluid.getHeight(state)
@ -147,7 +148,7 @@ class SubmersionState(private val physics: EntityPhysics<*>) : Tickable {
// TODO // TODO
} }
val position = physics.position val position = physics.position
val eyePosition = BlockPosition(position.x.floor, eyeHeight.floor, position.z.floor) val eyePosition = BlockPosition(position.x.floor, eyeHeight.floor.clamp(BlockPosition.MIN_Y, BlockPosition.MAX_Y), position.z.floor)
val block = physics.positionInfo.chunk?.get(eyePosition.inChunkPosition) ?: return val block = physics.positionInfo.chunk?.get(eyePosition.inChunkPosition) ?: return
if (block.block !is FluidHolder) { if (block.block !is FluidHolder) {