diff --git a/src/main/java/de/bixilon/minosoft/data/world/border/WorldBorder.kt b/src/main/java/de/bixilon/minosoft/data/world/border/WorldBorder.kt index 3805de8c8..dc94f97b5 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/border/WorldBorder.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/border/WorldBorder.kt @@ -16,10 +16,10 @@ package de.bixilon.minosoft.data.world.border import de.bixilon.kotlinglm.vec2.Vec2d import de.bixilon.kotlinglm.vec3.Vec3d import de.bixilon.kotlinglm.vec3.Vec3i -import de.bixilon.kutil.concurrent.lock.simple.SimpleLock -import de.bixilon.kutil.math.interpolation.DoubleInterpolation.interpolateLinear -import de.bixilon.kutil.time.TimeUtil.millis import de.bixilon.minosoft.data.world.World +import de.bixilon.minosoft.data.world.border.area.BorderArea +import de.bixilon.minosoft.data.world.border.area.DynamicBorderArea +import de.bixilon.minosoft.data.world.border.area.StaticBorderArea import de.bixilon.minosoft.data.world.positions.BlockPosition import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2dUtil.EMPTY import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition @@ -27,24 +27,11 @@ import kotlin.math.abs class WorldBorder { var center = Vec2d.EMPTY - var radius = DEFAULT_RADIUS var warningTime = 0 var warningBlocks = 0 var portalBound = 0 - var state = WorldBorderState.STATIC - private set - - var interpolationStart = -1L - private set - var interpolationEnd = -1L - private set - var oldRadius = DEFAULT_RADIUS - private set - var newRadius = DEFAULT_RADIUS - private set - - val lock = SimpleLock() + var area: BorderArea = StaticBorderArea(MAX_RADIUS) fun isOutside(blockPosition: Vec3i): Boolean { return isOutside(blockPosition.x.toDouble(), blockPosition.z.toDouble()) && isOutside(blockPosition.x + 1.0, blockPosition.z + 1.0) @@ -55,10 +42,9 @@ class WorldBorder { } fun isOutside(x: Double, z: Double): Boolean { - lock.acquire() - val radius = radius + val center = center + val radius = area.radius val inside = x in maxOf(-MAX_RADIUS, center.x - radius)..minOf(MAX_RADIUS, center.x + radius) && z in maxOf(-MAX_RADIUS, center.y - radius)..minOf(MAX_RADIUS, center.y + radius) - lock.release() return !inside } @@ -72,78 +58,32 @@ class WorldBorder { } fun getDistanceTo(x: Double, z: Double): Double { - lock.acquire() - val radius = radius + val center = center + val radius = area.radius - val closestDistance = minOf( + return minOf( minOf(MAX_RADIUS, radius - abs(center.x)) - abs(x), minOf(MAX_RADIUS, radius - abs(center.y)) - abs(z), ) - lock.release() - return closestDistance - } - - fun stopInterpolating() { - lock.lock() - interpolationStart = -1L - lock.unlock() } fun interpolate(oldRadius: Double, newRadius: Double, millis: Long) { - if (millis <= 0L) { - stopInterpolating() - radius = newRadius + if (millis <= 0L || oldRadius == newRadius) { + area = StaticBorderArea(newRadius) + return } - lock.lock() - val time = millis() - interpolationStart = time - interpolationEnd = time + millis - this.oldRadius = oldRadius - this.newRadius = newRadius - lock.unlock() + area = DynamicBorderArea(this, oldRadius, newRadius, millis) } fun tick() { - lock.lock() - if (interpolationStart < 0L) { - lock.unlock() - return - } - val time = millis() - if (interpolationEnd <= time) { - this.radius = newRadius // also get the last interpolation step - state = WorldBorderState.STATIC - interpolationStart = -1L - lock.unlock() - return - } - val oldRadius = radius - - val remaining = interpolationEnd - time - val totalTime = (interpolationEnd - interpolationStart) - val radius = interpolateLinear(remaining.toDouble() / totalTime.toDouble(), this.oldRadius, this.newRadius) - this.radius = radius - - state = if (oldRadius > newRadius) { - WorldBorderState.SHRINKING - } else if (oldRadius < newRadius) { - WorldBorderState.GROWING - } else { - interpolationStart = -1L - WorldBorderState.STATIC - } - lock.unlock() + area.tick() } fun reset() { - lock.lock() - radius = DEFAULT_RADIUS - interpolationStart = -1L - lock.unlock() + area = StaticBorderArea(MAX_RADIUS) } companion object { const val MAX_RADIUS = (World.MAX_SIZE - ProtocolDefinition.SECTION_WIDTH_X).toDouble() - const val DEFAULT_RADIUS = MAX_RADIUS } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/border/area/BorderArea.kt b/src/main/java/de/bixilon/minosoft/data/world/border/area/BorderArea.kt new file mode 100644 index 000000000..c7e692ae4 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/border/area/BorderArea.kt @@ -0,0 +1,27 @@ +/* + * Minosoft + * Copyright (C) 2020-2023 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.world.border.area + +import de.bixilon.kutil.time.TimeUtil.millis +import de.bixilon.minosoft.data.Tickable +import de.bixilon.minosoft.data.world.border.WorldBorderState + +interface BorderArea : Tickable { + val radius: Double + + val state: WorldBorderState + + + fun radius(time: Long = millis()): Double = radius +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/border/area/DynamicBorderArea.kt b/src/main/java/de/bixilon/minosoft/data/world/border/area/DynamicBorderArea.kt new file mode 100644 index 000000000..c34c07000 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/border/area/DynamicBorderArea.kt @@ -0,0 +1,57 @@ +/* + * Minosoft + * Copyright (C) 2020-2023 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.world.border.area + +import de.bixilon.kutil.math.interpolation.DoubleInterpolation.interpolateLinear +import de.bixilon.kutil.time.TimeUtil.millis +import de.bixilon.minosoft.data.world.border.WorldBorder +import de.bixilon.minosoft.data.world.border.WorldBorderState + +class DynamicBorderArea( + val border: WorldBorder, + val oldRadius: Double, + val newRadius: Double, + val millis: Long, +) : BorderArea { + val start: Long = millis() + val end = start + millis + + override var state: WorldBorderState = state() + override var radius: Double = oldRadius + + override fun radius(time: Long): Double { + return interpolateLinear(progress(time), oldRadius, newRadius) + } + + private fun progress(time: Long): Double { + return (time - start).toDouble() / (end - start) + } + + override fun tick() { + val time = millis() + if (end <= time) { + border.area = StaticBorderArea(newRadius) + return + } + + this.radius = interpolateLinear(progress(time), this.oldRadius, this.newRadius) + this.state = state() + } + + private fun state() = when { + oldRadius > newRadius -> WorldBorderState.SHRINKING + oldRadius < newRadius -> WorldBorderState.GROWING + else -> WorldBorderState.STATIC // impossible? + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/border/area/StaticBorderArea.kt b/src/main/java/de/bixilon/minosoft/data/world/border/area/StaticBorderArea.kt new file mode 100644 index 000000000..8f904cd90 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/border/area/StaticBorderArea.kt @@ -0,0 +1,24 @@ +/* + * Minosoft + * Copyright (C) 2020-2023 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.world.border.area + +import de.bixilon.minosoft.data.world.border.WorldBorderState + +class StaticBorderArea( + override val radius: Double, +) : BorderArea { + override val state: WorldBorderState get() = WorldBorderState.STATIC + + override fun tick() = Unit +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/world/border/WorldBorderRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/world/border/WorldBorderRenderer.kt index c6e77566d..76323bf93 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/world/border/WorldBorderRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/world/border/WorldBorderRenderer.kt @@ -15,7 +15,6 @@ package de.bixilon.minosoft.gui.rendering.world.border import de.bixilon.kotlinglm.func.common.clamp import de.bixilon.kutil.latch.CountUpAndDownLatch -import de.bixilon.kutil.math.interpolation.DoubleInterpolation.interpolateLinear import de.bixilon.kutil.observer.DataObserver.Companion.observe import de.bixilon.kutil.time.TimeUtil.millis import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft @@ -65,7 +64,7 @@ class WorldBorderRenderer( private fun calculateColor(): RGBColor { val distance = border.getDistanceTo(context.connection.player.physics.position).toFloat() - 1.0f // 1 block padding val strength = 1.0f - distance.clamp(0.0f, MAX_DISTANCE) / MAX_DISTANCE // slowly fade in - val color = when (border.state) { + val color = when (border.area.state) { WorldBorderState.GROWING -> GROWING_COLOR WorldBorderState.SHRINKING -> SHRINKING_COLOR WorldBorderState.STATIC -> STATIC_COLOR @@ -86,21 +85,12 @@ class WorldBorderRenderer( shader.tintColor = calculateColor() } - private fun getRadius(): Double { - val start = border.interpolationStart - if (start < 0L) return border.radius - - val progress = (millis() - start).toDouble() / (border.interpolationEnd - start) - - return interpolateLinear(progress, border.oldRadius, border.newRadius) - } - override fun prepareDrawAsync() { if (skipAll) return val center = border.center - val radius = getRadius() + val radius = border.area.radius() val previous = this.borderMesh if (previous != null && !reload && center == previous.center && radius == previous.radius) return diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/border/SizeWorldBorderS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/border/SizeWorldBorderS2CP.kt index 67dd16f23..c42872c75 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/border/SizeWorldBorderS2CP.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/border/SizeWorldBorderS2CP.kt @@ -13,6 +13,7 @@ package de.bixilon.minosoft.protocol.packets.s2c.play.border +import de.bixilon.minosoft.data.world.border.area.StaticBorderArea import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.packets.factory.LoadPacket import de.bixilon.minosoft.protocol.protocol.buffers.play.PlayInByteBuffer @@ -25,11 +26,10 @@ class SizeWorldBorderS2CP(buffer: PlayInByteBuffer) : WorldBorderS2CP { val radius = buffer.readDouble() / 2.0 override fun handle(connection: PlayConnection) { - connection.world.border.stopInterpolating() - connection.world.border.radius = radius + connection.world.border.area = StaticBorderArea(radius) } override fun log(reducedLog: Boolean) { - Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "Size set world border (diameter=$radius)" } + Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "Size set world border (radius=$radius)" } } }