diff --git a/README.md b/README.md index 7617105..e64ff7a 100644 --- a/README.md +++ b/README.md @@ -1 +1,15 @@ # Blokk + +## Conventions + +### KDoc + +1. If the name of the target is already sufficient for understanding what it does/represents, you should **not** add a comment. But if you want to provide additional information, you should start the comment with a short description nevertheless. +2. The name of the return type, property type or type of the enclosing class should **not** be wrapped in square + brackets. +3. If a comment only consists of the `@returns` block tag, the latter should be replaced with a sentence starting with + `Returns `. + +### Code + +1. If a member function of a class creates an instance of another class which represents the same value, the function’s name should be `as`. If the new instance does not exactly represent the value of the original instance (for example because it is rounded), the name should be `to`. diff --git a/blokk-api/src/main/kotlin/space/blokk/Vector.kt b/blokk-api/src/main/kotlin/space/blokk/Vector.kt new file mode 100644 index 0000000..f27ae26 --- /dev/null +++ b/blokk-api/src/main/kotlin/space/blokk/Vector.kt @@ -0,0 +1,55 @@ +package space.blokk + +import space.blokk.util.clamp +import space.blokk.world.Location +import space.blokk.world.VoxelLocation +import kotlin.math.abs +import kotlin.math.pow +import kotlin.math.roundToInt +import kotlin.math.sqrt + +data class Vector(val x: Double, val y: Double, val z: Double) { + /** + * Returns a VoxelLocation by converting [x], [y] and z to Int and clamping y between 0 and 255. + */ + fun toVoxelLocation() = VoxelLocation(x.toInt(), y.toInt().clamp(0..255).toUByte(), z.toInt()) + fun roundToBlock() = VoxelLocation(x.roundToInt(), y.toInt().clamp(0..255).toUByte(), z.roundToInt()) + fun asLocation() = Location(x, y, z) + + operator fun plus(other: Vector) = Vector(x + other.x, y + other.y, z + other.z) + operator fun minus(other: Vector) = Vector(x - other.x, y - other.y, z - other.z) + operator fun div(other: Vector) = Vector(x / other.x, y / other.y, z / other.z) + operator fun times(other: Vector) = Vector(x * other.x, y * other.y, z * other.z) + + operator fun get(part: CoordinatePart): Double = when (part) { + CoordinatePart.X -> x + CoordinatePart.Y -> y + CoordinatePart.Z -> z + } + + companion object { + /** + * Returns a Vector with the maximum x, y and z values of [a] and [b]. + */ + fun withHigherValuesOf(a: Vector, b: Vector) = Vector(maxOf(a.x, b.x), maxOf(a.y, b.y), maxOf(a.z, b.z)) + + /** + * Returns a Vector with the minimum x, y and z values of [a] and [b]. + */ + fun withLowerValuesOf(a: Vector, b: Vector) = Vector(minOf(a.x, b.x), minOf(a.y, b.y), minOf(a.z, b.z)) + + /** + * Returns the (euclidean) distance between [a] and [b]. + */ + fun distanceBetween(a: Vector, b: Vector): Double { + val (x, y, z) = abs(a - b) + + return sqrt(sqrt(x.pow(2) + y.pow(2)) + z.pow(2)) + } + } +} + +/** + * Returns a Vector with the absolute values of x, y and z. + */ +fun abs(vector: Vector) = Vector(abs(vector.x), abs(vector.y), abs(vector.z)) diff --git a/blokk-api/src/main/kotlin/space/blokk/util/Clamp.kt b/blokk-api/src/main/kotlin/space/blokk/util/Clamp.kt new file mode 100644 index 0000000..57c6c2d --- /dev/null +++ b/blokk-api/src/main/kotlin/space/blokk/util/Clamp.kt @@ -0,0 +1,3 @@ +package space.blokk.util + +fun Int.clamp(range: IntRange) = maxOf(minOf(range.first, range.last), minOf(maxOf(range.first, range.last), this)) diff --git a/blokk-api/src/main/kotlin/space/blokk/world/Location.kt b/blokk-api/src/main/kotlin/space/blokk/world/Location.kt index 3be7479..192bcf4 100644 --- a/blokk-api/src/main/kotlin/space/blokk/world/Location.kt +++ b/blokk-api/src/main/kotlin/space/blokk/world/Location.kt @@ -1,5 +1,7 @@ package space.blokk.world +import space.blokk.CoordinatePart +import space.blokk.Vector import kotlin.math.roundToInt sealed class AbstractWorldAndLocation { @@ -23,14 +25,14 @@ sealed class AbstractLocation { abstract val z: Double /** - * Converts this [Location] to a [VoxelLocation] by converting [x], [y] and [z] to an integer using [Double.toInt], + * Converts this Location to a VoxelLocation by converting [x], [y] and [z] to integers using [Double.toInt], * in contrast to [roundToBlock] which uses [Double.roundToInt]. */ - fun asVoxelLocation(): VoxelLocation = VoxelLocation(x.toInt(), y.toInt().toUByte(), z.toInt()) + fun toVoxelLocation(): VoxelLocation = VoxelLocation(x.toInt(), y.toInt().toUByte(), z.toInt()) /** - * Converts this [Location] to a [VoxelLocation] by rounding [x], [y] and [z] to an integer using - * [Double.roundToInt], in contrast to [asVoxelLocation] which uses [Double.toInt]. + * Converts this Location to a VoxelLocation by converting [x], [y] and [z] to integers using [Double.roundToInt], + * in contrast to [toVoxelLocation] which uses [Double.toInt]. */ fun roundToBlock(): VoxelLocation = VoxelLocation(x.roundToInt(), y.roundToInt().toUByte(), z.roundToInt()) @@ -43,6 +45,12 @@ sealed class AbstractLocation { * Returns a pair of [world] and this location. */ abstract infix fun inside(world: World): AbstractWorldAndLocation<*> + + operator fun get(part: CoordinatePart): Double = when (part) { + CoordinatePart.X -> x + CoordinatePart.Y -> y + CoordinatePart.Z -> z + } } /** @@ -52,6 +60,8 @@ sealed class AbstractLocation { */ data class Location(override val x: Double, override val y: Double, override val z: Double) : AbstractLocation() { override fun inside(world: World) = WorldAndLocation(world, this) + + fun asVector() = Vector(x, y, z) } data class LocationWithRotation( @@ -62,4 +72,6 @@ data class LocationWithRotation( val pitch: Float ) : AbstractLocation() { override fun inside(world: World) = WorldAndLocationWithRotation(world, this) + + fun toVector() = Vector(x, y, z) } diff --git a/blokk-api/src/main/kotlin/space/blokk/world/VoxelLocation.kt b/blokk-api/src/main/kotlin/space/blokk/world/VoxelLocation.kt index 3a3896c..9709982 100644 --- a/blokk-api/src/main/kotlin/space/blokk/world/VoxelLocation.kt +++ b/blokk-api/src/main/kotlin/space/blokk/world/VoxelLocation.kt @@ -1,44 +1,49 @@ package space.blokk.world import space.blokk.CoordinatePart +import space.blokk.Vector +/** + * A location consisting of an x, y and z coordinate. + * + * This class does not contain any functions for calculations, but [Vector] does. + * You can convert a VoxelLocation to a Vector using [asVector]. + */ data class VoxelLocation(val x: Int, val y: UByte, val z: Int) { - constructor(x: Int, y: Int, z: Int): this(x, y.toUByte(), z) - /** - * Converts this [VoxelLocation] to a [Location] by converting [x], [y] and [z] to a double using [Int.toDouble]. + * Converts this VoxelLocation to a */ fun asLocation(): Location = Location(x.toDouble(), y.toDouble(), z.toDouble()) + fun asVector(): Vector = Vector(x.toDouble(), y.toDouble(), z.toDouble()) + /** - * Converts this [VoxelLocation] to a [Location] by converting [x], [y] and [z] to a double using [Int.toDouble] - * and then adding 0.5 to each value. + * Converts this VoxelLocation to a Location **and then adds 0.5 to x, y and z**. * * Example: `VoxelLocation(x = 1, y = 2, z = 3)` becomes `Location(x = 1.5, y = 2.5, z = 3.5)`. */ fun atCenter(): Location = Location(x.toDouble() + 0.5, y.toDouble() + 0.5, z.toDouble() + 0.5) /** - * Converts this [VoxelLocation] to a [Location] by converting [x], [y] and [z] to a double using [Int.toDouble] - * and then adding 0.5 to x and z, but not y. + * Converts this VoxelLocation to a Location **and then adds 0.5 to x and z, but not y**. * * Example: `VoxelLocation(x = 1, y = 2, z = 3)` becomes `Location(x = 1.5, y = 2, z = 3.5)`. */ fun atTopCenter(): Location = Location(x.toDouble() + 0.5, y.toDouble(), z.toDouble() + 0.5) /** - * @return A new [VoxelLocation] with the maximum x, y and z values of a and b. + * Returns a VoxelLocation with the maximum x, y and z values of this and [other]. */ fun withHighestValues(other: VoxelLocation) = VoxelLocation(maxOf(x, other.x), maxOf(y, other.y), maxOf(z, other.z)) /** - * @return A new [VoxelLocation] with the minimum x, y and z values of a and b. + * Returns a VoxelLocation with the minimum x, y and z values of this and [other]. */ fun withLowestValues(other: VoxelLocation) = VoxelLocation(minOf(x, other.x), minOf(y, other.y), minOf(z, other.z)) - operator fun get(part: CoordinatePart) = when (part) { + operator fun get(part: CoordinatePart): Int = when (part) { CoordinatePart.X -> x - CoordinatePart.Y -> y.toInt() // Because otherwise the return type of this function would be Any + CoordinatePart.Y -> y.toInt() CoordinatePart.Z -> z } } diff --git a/blokk-api/src/main/kotlin/space/blokk/world/World.kt b/blokk-api/src/main/kotlin/space/blokk/world/World.kt index 51d2e43..4b1e38e 100644 --- a/blokk-api/src/main/kotlin/space/blokk/world/World.kt +++ b/blokk-api/src/main/kotlin/space/blokk/world/World.kt @@ -52,19 +52,23 @@ abstract class World(val uuid: UUID) { fun getVoxel(location: VoxelLocation): Voxel = getChunk(location).getVoxel(location) /** - * Returns all voxels in the cube with the corner points [firstCorner] and [secondCorner]. + * Returns all voxels in the cube with the corner points [cornerA] and [cornerB]. * - * @param order The nesting order of the arrays. + * @param hollow Whether the cube is hollow */ @OptIn(ExperimentalStdlibApi::class) - fun getVoxelsInCube(firstCorner: VoxelLocation, secondCorner: VoxelLocation): List { - val start = firstCorner.withLowestValues(secondCorner) - val end = firstCorner.withHighestValues(secondCorner) + fun getVoxelsInCube(cornerA: VoxelLocation, cornerB: VoxelLocation, hollow: Boolean = false): List { + val xList = if (hollow) listOf(cornerA.x, cornerB.x).distinct() else (cornerA.x..cornerB.x).toList() + val zList = if (hollow) listOf(cornerA.z, cornerB.z).distinct() else (cornerA.z..cornerB.z).toList() + + val yList = + if (hollow) listOf(cornerA.y.toInt(), cornerB.y.toInt()).distinct() + else (cornerA.y.toInt()..cornerB.y.toInt()).toList() return buildList { - for(x in start.x..end.x) { - for(y in start.y..end.y) { - for(z in start.z..end.z) { + for(x in xList) { + for(y in yList) { + for(z in zList) { add(getVoxel(VoxelLocation(x, y.toUByte(), z))) } } @@ -77,9 +81,10 @@ abstract class World(val uuid: UUID) { */ fun getVoxelsInSphere( center: VoxelLocation, - radius: Int + radius: Int, + hollow: Boolean ): List { - // https://www.reddit.com/r/VoxelGameDev/comments/2cttnt/how_to_create_a_sphere_out_of_voxels/ + // Implementation example https://git.io/JLysq TODO() } diff --git a/blokk-server/src/main/kotlin/space/blokk/net/LoginAndJoinProcedure.kt b/blokk-server/src/main/kotlin/space/blokk/net/LoginAndJoinProcedure.kt index ef1904c..8ed2da2 100644 --- a/blokk-server/src/main/kotlin/space/blokk/net/LoginAndJoinProcedure.kt +++ b/blokk-server/src/main/kotlin/space/blokk/net/LoginAndJoinProcedure.kt @@ -12,7 +12,6 @@ import space.blokk.net.event.SessionAfterLoginEvent import space.blokk.net.packet.login.* import space.blokk.net.packet.play.* import space.blokk.player.BlokkPlayer -import space.blokk.player.GameMode import space.blokk.tag.TagRegistry import space.blokk.util.AuthenticationHelper import space.blokk.util.EncryptionUtils @@ -199,7 +198,7 @@ class LoginAndJoinProcedure(val session: BlokkSession) { ) ) - session.send(UpdateViewPositionPacket(Chunk.Key.from(player.location.asVoxelLocation()))) + session.send(UpdateViewPositionPacket(Chunk.Key.from(player.location.toVoxelLocation()))) session.lastKeepAlivePacketTimestamp = System.currentTimeMillis() session.scheduleKeepAlivePacket() diff --git a/blokk-server/src/main/kotlin/space/blokk/player/BlokkPlayer.kt b/blokk-server/src/main/kotlin/space/blokk/player/BlokkPlayer.kt index 4a912be..0cbc91a 100644 --- a/blokk-server/src/main/kotlin/space/blokk/player/BlokkPlayer.kt +++ b/blokk-server/src/main/kotlin/space/blokk/player/BlokkPlayer.kt @@ -1,12 +1,9 @@ package space.blokk.player import space.blokk.chat.TextComponent -import space.blokk.event.EventBus -import space.blokk.logging.Logger import space.blokk.net.Session import space.blokk.net.packet.play.ChunkDataPacket import space.blokk.net.packet.play.ChunkLightDataPacket -import space.blokk.player.event.PlayerEvent import space.blokk.world.Chunk import space.blokk.world.LocationWithRotation import space.blokk.world.VoxelLocation @@ -53,7 +50,7 @@ class BlokkPlayer( */ @OptIn(ExperimentalStdlibApi::class) fun updateCurrentlyViewedChunks() { - val (centerX, centerZ) = Chunk.Key.from(location.asVoxelLocation()) + val (centerX, centerZ) = Chunk.Key.from(location.toVoxelLocation()) val edgeLength = settings.viewDistance + 1