From b5341bda91850304ac3aa021214536391a4db020 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Fri, 26 Jul 2024 12:40:35 -0400 Subject: [PATCH 01/11] feat: fast vector implementation Introduces a zero-allocation position tuple for low-level api world search --- .../lambda/module/modules/debug/BlockTest.kt | 42 +++++ .../lambda/module/modules/debug/RenderTest.kt | 10 +- .../com/lambda/util/collections/Extensions.kt | 15 +- .../lambda/util/primitives/extension/World.kt | 29 ++++ .../kotlin/com/lambda/util/world/Position.kt | 123 ++++++++++++++ .../kotlin/com/lambda/util/world/WorldDsl.kt | 157 ++++++++++++++++++ .../com/lambda/util/world/WorldUtils.kt | 114 +++++-------- 7 files changed, 410 insertions(+), 80 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt create mode 100644 common/src/main/kotlin/com/lambda/util/primitives/extension/World.kt create mode 100644 common/src/main/kotlin/com/lambda/util/world/Position.kt create mode 100644 common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt new file mode 100644 index 000000000..2c4347ee7 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt @@ -0,0 +1,42 @@ +package com.lambda.module.modules.debug + +import com.lambda.event.events.RenderEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.graphics.renderer.esp.builders.build +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.util.world.search +import net.minecraft.block.Blocks +import net.minecraft.util.math.Box +import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.Vec3i +import java.awt.Color + +object BlockTest : Module( + name = "BlockTest", + description = "BlockTest", + defaultTags = setOf(ModuleTag.DEBUG), +) { + private val rangeX by setting("Range X", 5.0, 0.1..7.0, 0.1, "Range X") + private val rangeY by setting("Range Y", 5.0, 0.1..7.0, 0.1, "Range Y") + private val rangeZ by setting("Range Z", 5.0, 0.1..7.0, 0.1, "Range Z") + private val stepX by setting("Step X", 1, 1..7, 1, "Step X") + private val stepY by setting("Step Y", 1, 1..7, 1, "Step Y") + private val stepZ by setting("Step Z", 1, 1..7, 1, "Step Z") + + private val filledColor = Color(100, 150, 255, 128) + private val outlineColor = Color(100, 150, 255, 51) + + init { + listener { + search { + blocks( + range = Vec3d(rangeX, rangeY, rangeZ), + step = Vec3i(stepX, stepY, stepZ), + ) { _, block -> block.isOf(Blocks.DIAMOND_BLOCK) }.forEach { (pos, _) -> + it.renderer.build(Box.of(pos, 1.0, 1.0, 1.0), filledColor, outlineColor) + } + } + } + } +} diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt index e014b2321..e691141a9 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt @@ -7,7 +7,7 @@ import com.lambda.graphics.renderer.esp.builders.build import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.math.ColorUtils.setAlpha -import com.lambda.util.world.WorldUtils.getClosestEntity +import com.lambda.util.world.search import net.minecraft.entity.LivingEntity import net.minecraft.util.math.Box import java.awt.Color @@ -29,12 +29,14 @@ object RenderTest : Module( init { listener { - val entity = getClosestEntity(player.pos, 8.0) ?: return@listener - it.renderer.build(entity.dynamicBox, filledColor, outlineColor) + search { + val entity = closestEntity(8.0) ?: return@search + it.renderer.build(entity.dynamicBox, filledColor, outlineColor) + } } listener { it.renderer.build(Box.of(player.pos, 0.3, 0.3, 0.3), filledColor, outlineColor) } } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt b/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt index 32753525a..6c4ab565f 100644 --- a/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt +++ b/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt @@ -20,16 +20,15 @@ inline fun > Iterable<*>.filterPointer( predicate: (R) -> Boolean, ) { var index = 0 + for (element in this) { - when { - element is R && predicate(element) && destination != null -> { - iterator(element, index++) - destination.add(element) - } + val fulfilled = predicate(element as? R ?: continue) - element is R && predicate(element) && destination == null -> { - iterator(element, index++) - } + if (fulfilled && destination != null) { + destination.add(element) + iterator(element, index) } + + index++ } } diff --git a/common/src/main/kotlin/com/lambda/util/primitives/extension/World.kt b/common/src/main/kotlin/com/lambda/util/primitives/extension/World.kt new file mode 100644 index 000000000..db5dfd1f1 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/primitives/extension/World.kt @@ -0,0 +1,29 @@ +package com.lambda.util.primitives.extension + +import net.minecraft.block.BlockState +import net.minecraft.block.Blocks +import net.minecraft.fluid.FluidState +import net.minecraft.fluid.Fluids +import net.minecraft.world.World + +fun World.getBlockState(x: Int, y: Int, z: Int): BlockState { + if (isOutOfHeightLimit(y)) return Blocks.VOID_AIR.defaultState + + val chunk = getChunk(x shr 4, z shr 4) + val sectionIndex = getSectionIndex(y) + if (sectionIndex < 0) return Blocks.VOID_AIR.defaultState + + val section = chunk.getSection(sectionIndex) + return section.getBlockState(x and 0xF, y and 0xF, z and 0xF) +} + +fun World.getFluidState(x: Int, y: Int, z: Int): FluidState { + if (isOutOfHeightLimit(y)) return Fluids.EMPTY.defaultState + + val chunk = getChunk(x shr 4, z shr 4) + val sectionIndex = getSectionIndex(y) + if (sectionIndex < 0) return Fluids.EMPTY.defaultState + + val section = chunk.getSection(sectionIndex) + return section.getFluidState(x and 0xF, y and 0xF, z and 0xF) +} diff --git a/common/src/main/kotlin/com/lambda/util/world/Position.kt b/common/src/main/kotlin/com/lambda/util/world/Position.kt new file mode 100644 index 000000000..892ced221 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/world/Position.kt @@ -0,0 +1,123 @@ +package com.lambda.util.world + +import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.Vec3i + +/** + * Represents a position in the world encoded as a long. + * + * [ X (26 bits) | Y (12 bits) | Z (26 bits) ] + * + * The position is encoded as a 64-bit long where the X and Z coordinates are stored in the most significant 26 bits + * and the Y coordinate is stored in the middle 12 bits. This encoding allows for a maximum world size of + * ±33,554,432 blocks in the X and Z directions and ±2,048 blocks in the Y direction, which is more than + * needed. + */ +typealias FastVector = Long + +const val X_BITS = 26 +const val Y_BITS = 12 +const val Z_BITS = 26 + +const val X_SHIFT = Y_BITS + Z_BITS +const val Y_SHIFT = Z_BITS + +const val X_MASK = (1L shl X_BITS) - 1L +const val Y_MASK = (1L shl Y_BITS) - 1L +const val Z_MASK = (1L shl Z_BITS) - 1L + +/** + * Gets the X coordinate from the position. + */ +val FastVector.x: Long + get() = this shr X_SHIFT + +/** + * Gets the X coordinate from the position. + */ +val FastVector.xInt: Int + get() = x.toInt() + +/** + * Gets the Y coordinate from the position. + */ +val FastVector.y: Long + get() = this shr Y_SHIFT and Y_MASK + +/** + * Gets the Y coordinate from the position. + */ +val FastVector.yInt: Int + get() = y.toInt() + +/** + * Gets the Z coordinate from the position. + */ +val FastVector.z: Long + get() = this shl (64 - Z_BITS) shr (64 - Z_BITS) + +/** + * Gets the Z coordinate from the position. + */ +val FastVector.zInt + get() = z.toInt() + +/** + * Adds the given value to the X coordinate. + */ +fun FastVector.addX(value: Long): FastVector = fastVectorOf(x + value, y, z) + +/** + * Adds the given value to the Y coordinate. + */ +fun FastVector.addY(value: Long): FastVector = fastVectorOf(x, y + value, z) + +/** + * Adds the given value to the Z coordinate. + */ +fun FastVector.addZ(value: Long): FastVector = fastVectorOf(x, y, z + value) + +/** + * Adds the given vector to the position. + * @return The new position. + */ +fun FastVector.add(vec: FastVector): FastVector = fastVectorOf(x + vec.x, y + vec.y, z + vec.z) + +/** + * Adds the given vector to the position. + * @return The new position. + */ +fun FastVector.add(vec: Vec3i): FastVector = fastVectorOf(x + vec.x, y + vec.y, z + vec.z) + +/** + * Creates a new position from the given coordinates. + */ +fun fastVectorOf(x: Long, y: Long, z: Long): FastVector = + ((x and X_MASK) shl X_SHIFT) or ((y and Y_MASK) shl Y_SHIFT) or (z and Z_MASK) + +/** + * Creates a new position from the given coordinates. + */ +fun fastVectorOf(x: Int, y: Int, z: Int): FastVector = fastVectorOf(x.toLong(), y.toLong(), z.toLong()) + +/** + * Encodes the vector as a position. + * @return The encoded position. + */ +fun Vec3i.encoded(): FastVector = fastVectorOf(x.toLong(), y.toLong(), z.toLong()) + +/** + * Encodes the vector as a position. + * @return The encoded position. + */ +fun Vec3d.encoded(): FastVector = fastVectorOf(x.toLong(), y.toLong(), z.toLong()) + +/** + * Decodes the position into a vector. + */ +fun FastVector.decoded(): Vec3d = Vec3d(x.toDouble(), y.toDouble(), z.toDouble()) + +/** + * Decodes the position into a vector. + */ +fun FastVector.decodedInt(): Vec3i = Vec3i(x.toInt(), y.toInt(), z.toInt()) diff --git a/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt b/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt new file mode 100644 index 000000000..614ad6d9c --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt @@ -0,0 +1,157 @@ +package com.lambda.util.world + +import com.lambda.context.SafeContext +import com.lambda.util.world.WorldUtils.getClosestEntity +import com.lambda.util.world.WorldUtils.getFastEntities +import com.lambda.util.world.WorldUtils.searchBlocks +import com.lambda.util.world.WorldUtils.searchFluids +import net.minecraft.block.BlockState +import net.minecraft.entity.Entity +import net.minecraft.fluid.Fluid +import net.minecraft.fluid.FluidState +import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.Vec3i + +@DslMarker +annotation class WorldDsl + +@WorldDsl +class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { + /** + * Get the closest entity to the specified position. + * @param range The range to search in. + * @param predicate The predicate to filter the entities. + */ + inline fun closestEntity( + range: Double, + predicate: (T) -> Boolean = { true }, + ): T? = safeContext.getClosestEntity(pos, range, predicate) + + /** + * Get all entities in the specified range. + * @param range The range to search in. + * @param predicate The predicate to filter the entities. + * @return A list of entities found. + */ + inline fun entities( + range: Double, + predicate: (T) -> Boolean = { true }, + ): List { + val entities = mutableListOf() + safeContext.getFastEntities(pos, range, entities, predicate = predicate) + return entities + } + + /** + * Search for blocks in the specified range. + * @param range The range to search in. + * @param step The step to search with. + * @param predicate The predicate to filter the blocks. + * @return A map of the blocks found to positions. + */ + inline fun blocks( + range: Int, + step: Int = 1, + crossinline predicate: (Vec3d, BlockState) -> Boolean = { _, _ -> true } + ): Map = blocks( + Vec3d(range.toDouble(), range.toDouble(), range.toDouble()), + Vec3d(step.toDouble(), step.toDouble(), step.toDouble()), + predicate + ) + + /** + * Search for blocks in the specified range. + * @param range The range to search in. + * @param step The step to search with. + * @param predicate The predicate to filter the blocks. + * @return A map of the blocks found to positions. + */ + inline fun blocks( + range: Vec3d, + step: Vec3i = Vec3i(1, 1, 1), + crossinline predicate: (Vec3d, BlockState) -> Boolean = { _, _ -> true } + ): Map = blocks(range, Vec3d(step.x.toDouble(), step.y.toDouble(), step.z.toDouble()), predicate) + + /** + * Search for blocks in the specified range. + * @param range The range to search in. + * @param step The step to search with. + * @param predicate The predicate to filter the blocks. + * @param iterator The iterator to call for each block found. + * @return A map of the blocks found to positions. + */ + inline fun blocks( + range: Vec3d, + step: Vec3d = Vec3d(1.0, 1.0, 1.0), + crossinline predicate: (Vec3d, BlockState) -> Boolean = { _, _ -> true }, + crossinline iterator: (Vec3d, BlockState) -> Unit = { _, _ -> }, + ): Map { + val blocks = mutableMapOf() + + val transformedPredicate: (FastVector, BlockState, Int) -> Boolean = + { fast, state, _ -> predicate(fast.decoded(), state) } + val transformedIterator: (FastVector, BlockState, Int) -> Unit = + { fast, state, _ -> iterator(fast.decoded(), state) } + + safeContext.searchBlocks(pos.encoded(), range.encoded(), step.encoded(), blocks, transformedPredicate, transformedIterator) + return blocks.mapKeys { it.key.decoded() } + } + + /** + * Search for fluids in the specified range. + * @param range The range to search in. + * @param step The step to search with. + * @param predicate The predicate to filter the fluids. + * @return A map of the fluids found to positions. + */ + inline fun fluids( + range: Int, + step: Int = 1, + crossinline predicate: (Vec3d, FluidState) -> Boolean = { _, _ -> true }, + ): Map = fluids( + Vec3d(range.toDouble(), range.toDouble(), range.toDouble()), + Vec3d(step.toDouble(), step.toDouble(), step.toDouble()), + predicate + ) + + /** + * Search for fluids in the specified range. + * @param range The range to search in. + * @param step The step to search with. + * @param predicate The predicate to filter the fluids. + * @return A map of the fluids found to positions. + */ + inline fun fluids( + range: Vec3d, + step: Vec3i = Vec3i(1, 1, 1), + crossinline predicate: (Vec3d, FluidState) -> Boolean = { _, _ -> true }, + ): Map = fluids(range, Vec3d(step.x.toDouble(), step.y.toDouble(), step.z.toDouble()), predicate) + + /** + * Search for fluids in the specified range. + * @param range The range to search in. + * @param step The step to search with. + * @param predicate The predicate to filter the fluids. + * @param iterator The iterator to call for each fluid found. + * @return A map of the fluids found to positions. + */ + inline fun fluids( + range: Vec3d, + step: Vec3d = Vec3d(1.0, 1.0, 1.0), + crossinline predicate: (Vec3d, FluidState) -> Boolean = { _, _ -> true }, + crossinline iterator: (Vec3d, FluidState) -> Unit = { _, _ -> }, + ): Map { + val fluids = mutableMapOf() + + val transformedPredicate: (FastVector, FluidState, Int) -> Boolean = + { fast, state, _ -> predicate(fast.decoded(), state) } + val transformedIterator: (FastVector, FluidState, Int) -> Unit = + { fast, state, _ -> iterator(fast.decoded(), state) } + + safeContext.searchFluids(pos.encoded(), range.encoded(), step.encoded(), fluids, transformedPredicate, transformedIterator) + return fluids.mapKeys { it.key.decoded() } + } +} + +fun SafeContext.search(pos: Vec3d = player.pos, block: SearchContext.() -> Unit) = + SearchContext(this, pos).apply(block) diff --git a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt index 4cd0e1c6a..3079d1924 100644 --- a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt @@ -1,18 +1,15 @@ package com.lambda.util.world import com.lambda.context.SafeContext -import com.lambda.util.BlockUtils.blockPos -import com.lambda.util.BlockUtils.blockState import com.lambda.util.collections.filterPointer -import net.minecraft.block.Block +import com.lambda.util.primitives.extension.getBlockState +import com.lambda.util.primitives.extension.getFluidState import net.minecraft.block.BlockState import net.minecraft.entity.Entity import net.minecraft.fluid.Fluid import net.minecraft.fluid.FluidState -import net.minecraft.util.math.BlockPos import net.minecraft.util.math.ChunkSectionPos import net.minecraft.util.math.Vec3d -import net.minecraft.util.math.Vec3i import kotlin.math.ceil /** @@ -33,16 +30,14 @@ import kotlin.math.ceil * When you create a new object, the JVM allocates memory for it on the heap. * When it is no longer needed, the garbage collector frees up the memory. * This process **IS** expensive, especially if you are creating and discarding many objects - * in a short period of time, for example, when retrieving all the 200 pigs in your farm every game tick. + * + * Please note that the author of this code currently does not have any certifications in the field of computer science. + * Simply plain old experience and knowledge. * * @see IBM - Pass By Reference * @see Florida State University - Pass By Reference vs. Pass By Value * @see IBM - Garbage Collection Impacts on Java Performance * @see Medium - GC and Its Effect on Java Performance - * - * Many functions uses a branching approach to avoid trolling the speculative execution of the CPU. - * @see Branch Predictor - * @see Speculative Execution */ object WorldUtils { /** @@ -60,7 +55,6 @@ object WorldUtils { pos: Vec3d, range: Double, predicate: (T) -> Boolean = { true }, - type: Class = T::class.java, ): T? { var closest: T? = null var closestDistance = Double.MAX_VALUE @@ -112,7 +106,6 @@ object WorldUtils { pointer: MutableList? = null, iterator: (T, Int) -> Unit = { _, _ -> }, predicate: (T) -> Boolean = { true }, - type: Class = T::class.java, ) { val chunks = ceil(distance / 16).toInt() val sectionX = pos.x.toInt() shr 4 @@ -156,7 +149,6 @@ object WorldUtils { pointer: MutableList? = null, iterator: (T, Int) -> Unit = { _, _ -> }, predicate: (T) -> Boolean = { true }, - type: Class = T::class.java, ) { world.entities.filterPointer(pointer, iterator) { entity -> entity != player && @@ -165,27 +157,6 @@ object WorldUtils { } } - /** - * Returns all the blocks and positions within the range where the predicate is true. - * - * @param pos The position to search from. - * @param rangeX The maximum distance to search for entities in the x-axis. - * @param rangeY The maximum distance to search for entities in the y-axis. - * @param rangeZ The maximum distance to search for entities in the z-axis. - * @param pointer The mutable map to store the positions to blocks in. - * @param predicate Predicate to filter the blocks. - * @param iterator Iterator to perform operations on each block. - */ - inline fun SafeContext.searchBlocks( - pos: Vec3i, - rangeX: Int, - rangeY: Int, - rangeZ: Int, - pointer: MutableMap? = null, - predicate: (BlockState, BlockPos) -> Boolean = { _, _ -> true }, - iterator: (BlockState, BlockPos, Int) -> Unit = { _, _, _ -> }, - ) = searchBlocks(pos, Vec3i(rangeX, rangeY, rangeZ), pointer, predicate, iterator) - /** * Returns all the blocks and positions within the range where the predicate is true. * @@ -196,22 +167,20 @@ object WorldUtils { * @param predicate Predicate to filter the blocks. */ inline fun SafeContext.searchBlocks( - pos: Vec3i, - range: Vec3i, - pointer: MutableMap? = null, - predicate: (BlockState, BlockPos) -> Boolean = { _, _ -> true }, - iterator: (BlockState, BlockPos, Int) -> Unit = { _, _, _ -> }, + pos: FastVector, + range: FastVector, + step: FastVector = 274945015809L, + pointer: MutableMap? = null, + predicate: (FastVector, BlockState, Int) -> Boolean = { _, _, _ -> true }, + iterator: (FastVector, BlockState, Int) -> Unit = { _, _, _ -> }, ) { - iteratePositions(pos, range) { blockPos, index -> - val state = blockPos.blockState(world) - when { - predicate(state, blockPos) && pointer != null -> { - pointer[blockPos] = state.block - iterator(state, blockPos, index) - } + iteratePositions(pos, range, step) { position, index -> + world.getBlockState(position.xInt, position.yInt, position.zInt).let { state -> + val fulfilled = predicate(position, state, index) - predicate(state, blockPos) && pointer == null -> { - iterator(state, blockPos, index) + if (fulfilled && pointer != null) { + pointer[position] = state + iterator(position, state, index) } } } @@ -227,22 +196,20 @@ object WorldUtils { * @param predicate Predicate to filter the fluids. */ inline fun SafeContext.searchFluids( - pos: Vec3i, - range: Vec3i, - pointer: MutableMap? = null, - iterator: (FluidState, BlockPos, Int) -> Unit = { _, _, _ -> }, - predicate: (FluidState, BlockPos) -> Boolean = { _, _ -> true }, + pos: FastVector, + range: FastVector, + step: FastVector = 274945015809L, + pointer: MutableMap? = null, + predicate: (FastVector, FluidState, Int) -> Boolean = { _, _, _ -> true }, + iterator: (FastVector, FluidState, Int) -> Unit = { _, _, _ -> }, ) { - iteratePositions(pos, range) { blockPos, index -> - val state = world.getFluidState(blockPos) - when { - predicate(state, blockPos) && pointer != null -> { - iterator(state, blockPos, index) - pointer[blockPos] = state.fluid as T - } + iteratePositions(pos, range, step) { position, index -> + world.getFluidState(position.xInt, position.yInt, position.zInt).let { state -> + val fulfilled = predicate(position, state, index) - predicate(state, blockPos) && pointer == null -> { - iterator(state, blockPos, index) + if (fulfilled && pointer != null) { + pointer[position] = state.fluid as T + iterator(position, state, index) } } } @@ -252,16 +219,27 @@ object WorldUtils { * Iterates over all positions within the specified range. * @param pos The position to start from. * @param range The maximum distance to search for entities in each axis. + * @param step The step to increment the position by. * @param iterator Iterator to perform operations on each position. */ inline fun iteratePositions( - pos: Vec3i, - range: Vec3i, - iterator: (BlockPos, Int) -> Unit, + pos: FastVector, + range: FastVector, + step: FastVector, + iterator: (FastVector, Int) -> Unit, ) { - BlockPos.iterateOutwards(pos.blockPos, range.x, range.y, range.z) - .forEachIndexed { index, blockPos -> - iterator(blockPos, index) + var index = 0 + + for (x in -range.x..range.x step step.x) { + for (y in -range.y..range.y step step.y) { + for (z in -range.z..range.z step step.z) { + iterator( + pos.add(fastVectorOf(x, y, z)), + index++ + ) + } } + } } } + From 329d1ba98d0d7defddb45d4e3280ff6cf8a0f20a Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Fri, 26 Jul 2024 13:09:55 -0400 Subject: [PATCH 02/11] default values --- common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt index 3079d1924..84ba49b2e 100644 --- a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt @@ -225,8 +225,8 @@ object WorldUtils { inline fun iteratePositions( pos: FastVector, range: FastVector, - step: FastVector, - iterator: (FastVector, Int) -> Unit, + step: FastVector = 274945015809L, + iterator: (FastVector, Int) -> Unit = { _, _ -> }, ) { var index = 0 From 46e3b10bd9c6345b7fa7458ec2246c1aac11c948 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Fri, 26 Jul 2024 13:10:07 -0400 Subject: [PATCH 03/11] convert vec3 to fastvec --- common/src/main/kotlin/com/lambda/util/world/Position.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/util/world/Position.kt b/common/src/main/kotlin/com/lambda/util/world/Position.kt index 892ced221..abb5cc129 100644 --- a/common/src/main/kotlin/com/lambda/util/world/Position.kt +++ b/common/src/main/kotlin/com/lambda/util/world/Position.kt @@ -100,6 +100,11 @@ fun fastVectorOf(x: Long, y: Long, z: Long): FastVector = */ fun fastVectorOf(x: Int, y: Int, z: Int): FastVector = fastVectorOf(x.toLong(), y.toLong(), z.toLong()) +/** + * Creates a new position from the given Vec3d. + */ +fun fastVectorOf(pos: Vec3d): FastVector = fastVectorOf(pos.x.toLong(), pos.y.toLong(), pos.z.toLong()) + /** * Encodes the vector as a position. * @return The encoded position. From 0c277e445ed9be1ee2b1ee383b555ebaeadaebc3 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sat, 17 Aug 2024 14:17:18 -0400 Subject: [PATCH 04/11] refactor: signed bits masking --- .../kotlin/com/lambda/util/world/Position.kt | 116 ++++++++++-------- .../kotlin/com/lambda/util/world/WorldDsl.kt | 9 ++ .../com/lambda/util/world/WorldUtils.kt | 6 +- 3 files changed, 76 insertions(+), 55 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/util/world/Position.kt b/common/src/main/kotlin/com/lambda/util/world/Position.kt index abb5cc129..8adc75fcd 100644 --- a/common/src/main/kotlin/com/lambda/util/world/Position.kt +++ b/common/src/main/kotlin/com/lambda/util/world/Position.kt @@ -6,7 +6,7 @@ import net.minecraft.util.math.Vec3i /** * Represents a position in the world encoded as a long. * - * [ X (26 bits) | Y (12 bits) | Z (26 bits) ] + * [ X (26 bits) | Z (26 bits) | Y (12 bits) ] * * The position is encoded as a 64-bit long where the X and Z coordinates are stored in the most significant 26 bits * and the Y coordinate is stored in the middle 12 bits. This encoding allows for a maximum world size of @@ -15,95 +15,112 @@ import net.minecraft.util.math.Vec3i */ typealias FastVector = Long -const val X_BITS = 26 -const val Y_BITS = 12 -const val Z_BITS = 26 +internal const val X_BITS = 26 +internal const val Z_BITS = 26 +internal const val Y_BITS = 12 -const val X_SHIFT = Y_BITS + Z_BITS -const val Y_SHIFT = Z_BITS +internal const val X_SHIFT = Y_BITS + Z_BITS +internal const val Z_SHIFT = Y_BITS -const val X_MASK = (1L shl X_BITS) - 1L -const val Y_MASK = (1L shl Y_BITS) - 1L -const val Z_MASK = (1L shl Z_BITS) - 1L +internal const val X_MASK = (1L shl X_BITS) - 1L +internal const val Z_MASK = (1L shl Z_BITS) - 1L +internal const val Y_MASK = (1L shl Y_BITS) - 1L -/** - * Gets the X coordinate from the position. - */ -val FastVector.x: Long - get() = this shr X_SHIFT +internal const val MIN_X = -(1L shl X_BITS - 1) +internal const val MIN_Z = -(1L shl Z_BITS - 1) +internal const val MIN_Y = -(1L shl Y_BITS - 1) +internal const val MAX_X = (1L shl X_BITS - 1) - 1L +internal const val MAX_Z = (1L shl Z_BITS - 1) - 1L +internal const val MAX_Y = (1L shl Y_BITS - 1) - 1L + +internal fun Long.bitSetTo(value: Long, position: Int, length: Int): Long { + val mask = (1L shl length) - 1L + return this and (mask shl position).inv() or (value and mask shl position) +} /** - * Gets the X coordinate from the position. + * Creates a new position from the given coordinates. */ -val FastVector.xInt: Int - get() = x.toInt() +fun fastVectorOf(x: Long, y: Long, z: Long): FastVector { + require(x in MIN_X..MAX_X) { "X coordinate out of bounds for $X_BITS bits: $x" } + require(y in MIN_Y..MAX_Y) { "Y coordinate out of bounds for $Y_BITS bits: $y" } + require(z in MIN_Z..MAX_Z) { "Z coordinate out of bounds for $Z_BITS bits: $z" } + + return ((x and X_MASK) shl X_SHIFT) or ((z and Z_MASK) shl Z_SHIFT) or (y and Y_MASK) +} /** - * Gets the Y coordinate from the position. + * Creates a new position from the given coordinates. */ -val FastVector.y: Long - get() = this shr Y_SHIFT and Y_MASK +fun fastVectorOf(x: Int, y: Int, z: Int): FastVector = fastVectorOf(x.toLong(), y.toLong(), z.toLong()) /** - * Gets the Y coordinate from the position. + * Gets the X coordinate from the position. */ -val FastVector.yInt: Int - get() = y.toInt() +val FastVector.x: Int + get() { + val x = (this shr X_SHIFT and X_MASK).toInt() + return if (x and (1 shl X_BITS - 1) != 0) x - (1 shl X_BITS) else x + } /** * Gets the Z coordinate from the position. */ -val FastVector.z: Long - get() = this shl (64 - Z_BITS) shr (64 - Z_BITS) +val FastVector.z: Int + get() { + val z = (this shr Z_SHIFT and Z_MASK).toInt() + return if (z and (1 shl Z_BITS - 1) != 0) z - (1 shl Z_BITS) else z + } /** - * Gets the Z coordinate from the position. + * Gets the Y coordinate from the position. */ -val FastVector.zInt - get() = z.toInt() +val FastVector.y: Int + get() { + val y = (this and Y_MASK).toInt() + return if (y and (1 shl Y_BITS - 1) != 0) y - (1 shl Y_BITS) else y + } /** - * Adds the given value to the X coordinate. + * Sets the X coordinate of the position. */ -fun FastVector.addX(value: Long): FastVector = fastVectorOf(x + value, y, z) +infix fun FastVector.withX(x: Int): FastVector = bitSetTo(x.toLong(), X_SHIFT, X_BITS) /** - * Adds the given value to the Y coordinate. + * Sets the Z coordinate of the position. */ -fun FastVector.addY(value: Long): FastVector = fastVectorOf(x, y + value, z) +infix fun FastVector.withZ(z: Int): FastVector = bitSetTo(z.toLong(), Z_SHIFT, Z_BITS) /** - * Adds the given value to the Z coordinate. + * Sets the Y coordinate of the position. */ -fun FastVector.addZ(value: Long): FastVector = fastVectorOf(x, y, z + value) +infix fun FastVector.withY(y: Int): FastVector = bitSetTo(y.toLong(), 0, Y_BITS) /** - * Adds the given vector to the position. - * @return The new position. + * Adds the given value to the X coordinate. */ -fun FastVector.add(vec: FastVector): FastVector = fastVectorOf(x + vec.x, y + vec.y, z + vec.z) +infix fun FastVector.addX(value: Int): FastVector = withX(x + value) /** - * Adds the given vector to the position. - * @return The new position. + * Adds the given value to the Z coordinate. */ -fun FastVector.add(vec: Vec3i): FastVector = fastVectorOf(x + vec.x, y + vec.y, z + vec.z) +infix fun FastVector.addZ(value: Int): FastVector = withZ(z + value) /** - * Creates a new position from the given coordinates. + * Adds the given value to the Y coordinate. */ -fun fastVectorOf(x: Long, y: Long, z: Long): FastVector = - ((x and X_MASK) shl X_SHIFT) or ((y and Y_MASK) shl Y_SHIFT) or (z and Z_MASK) +infix fun FastVector.addY(value: Int): FastVector = withY(y + value) /** - * Creates a new position from the given coordinates. + * Adds the given vector to the position. */ -fun fastVectorOf(x: Int, y: Int, z: Int): FastVector = fastVectorOf(x.toLong(), y.toLong(), z.toLong()) +infix fun FastVector.plus(vec: FastVector): FastVector = fastVectorOf(x + vec.x, y + vec.y, z + vec.z) /** - * Creates a new position from the given Vec3d. + * Adds the given vector to the position. + * @return The new position. */ -fun fastVectorOf(pos: Vec3d): FastVector = fastVectorOf(pos.x.toLong(), pos.y.toLong(), pos.z.toLong()) +infix fun FastVector.plus(vec: Vec3i): FastVector = fastVectorOf(x + vec.x, y + vec.y, z + vec.z) /** * Encodes the vector as a position. @@ -121,8 +138,3 @@ fun Vec3d.encoded(): FastVector = fastVectorOf(x.toLong(), y.toLong(), z.toLong( * Decodes the position into a vector. */ fun FastVector.decoded(): Vec3d = Vec3d(x.toDouble(), y.toDouble(), z.toDouble()) - -/** - * Decodes the position into a vector. - */ -fun FastVector.decodedInt(): Vec3i = Vec3i(x.toInt(), y.toInt(), z.toInt()) diff --git a/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt b/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt index 614ad6d9c..b1c86c9c5 100644 --- a/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt +++ b/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt @@ -15,6 +15,14 @@ import net.minecraft.util.math.Vec3i @DslMarker annotation class WorldDsl +/** + * Represents a search context in the world. + * These functions are meant for high-level interactions with the world + * at the cost of generally lower performances. + * + * @param safeContext The safe context to use. + * @param pos The position to search from. + */ @WorldDsl class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { /** @@ -74,6 +82,7 @@ class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { /** * Search for blocks in the specified range. + * * @param range The range to search in. * @param step The step to search with. * @param predicate The predicate to filter the blocks. diff --git a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt index 84ba49b2e..2d698eb78 100644 --- a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt @@ -175,7 +175,7 @@ object WorldUtils { iterator: (FastVector, BlockState, Int) -> Unit = { _, _, _ -> }, ) { iteratePositions(pos, range, step) { position, index -> - world.getBlockState(position.xInt, position.yInt, position.zInt).let { state -> + world.getBlockState(position.x, position.y, position.z).let { state -> val fulfilled = predicate(position, state, index) if (fulfilled && pointer != null) { @@ -204,7 +204,7 @@ object WorldUtils { iterator: (FastVector, FluidState, Int) -> Unit = { _, _, _ -> }, ) { iteratePositions(pos, range, step) { position, index -> - world.getFluidState(position.xInt, position.yInt, position.zInt).let { state -> + world.getFluidState(position.x, position.y, position.z).let { state -> val fulfilled = predicate(position, state, index) if (fulfilled && pointer != null) { @@ -234,7 +234,7 @@ object WorldUtils { for (y in -range.y..range.y step step.y) { for (z in -range.z..range.z step step.z) { iterator( - pos.add(fastVectorOf(x, y, z)), + pos plus fastVectorOf(x, y, z), index++ ) } From a7aa06966751fc4eb536f15887d2be6482ab08c2 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sat, 17 Aug 2024 14:36:08 -0400 Subject: [PATCH 05/11] refactor: documentation --- .../kotlin/com/lambda/util/world/WorldDsl.kt | 143 ++++++++++++------ .../com/lambda/util/world/WorldUtils.kt | 22 +-- 2 files changed, 104 insertions(+), 61 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt b/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt index b1c86c9c5..f7e687e96 100644 --- a/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt +++ b/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt @@ -1,7 +1,9 @@ package com.lambda.util.world +import com.lambda.Lambda.LOG import com.lambda.context.SafeContext import com.lambda.util.world.WorldUtils.getClosestEntity +import com.lambda.util.world.WorldUtils.getEntities import com.lambda.util.world.WorldUtils.getFastEntities import com.lambda.util.world.WorldUtils.searchBlocks import com.lambda.util.world.WorldUtils.searchFluids @@ -16,19 +18,21 @@ import net.minecraft.util.math.Vec3i annotation class WorldDsl /** - * Represents a search context in the world. - * These functions are meant for high-level interactions with the world - * at the cost of generally lower performances. + * A context for performing various high-level search operations within the world. + * These functions prioritize ease of use and flexibility, potentially at the cost + * of performance. * - * @param safeContext The safe context to use. - * @param pos The position to search from. + * @property safeContext The context that ensures safe operations within the world. + * @property pos The position from which searches are conducted. */ @WorldDsl class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { /** - * Get the closest entity to the specified position. - * @param range The range to search in. - * @param predicate The predicate to filter the entities. + * Finds the closest entity of type [T] to the specified position within a given range. + * + * @param range The maximum distance from the position to search for entities. + * @param predicate A filter to determine which entities to include in the search. + * @return The closest entity of type [T] that matches the predicate, or null if no entity is found. */ inline fun closestEntity( range: Double, @@ -36,26 +40,53 @@ class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { ): T? = safeContext.getClosestEntity(pos, range, predicate) /** - * Get all entities in the specified range. - * @param range The range to search in. - * @param predicate The predicate to filter the entities. - * @return A list of entities found. + * Retrieves a list of nearby entities of type [T] within the specified range. + * + * @param range The radius around the position to search for entities. + * @param predicate A filter to determine which entities to include in the search. + * @return A list of entities of type [T] */ - inline fun entities( + inline fun nearbyEntities( range: Double, predicate: (T) -> Boolean = { true }, ): List { val entities = mutableListOf() + + if (range >= 64) { + LOG.warn("Searching nearby entities with a range of $range may be slow. " + + "Consider reducing the range to improve performance or using a linear search instead.") + } + safeContext.getFastEntities(pos, range, entities, predicate = predicate) + return entities } /** - * Search for blocks in the specified range. - * @param range The range to search in. - * @param step The step to search with. - * @param predicate The predicate to filter the blocks. - * @return A map of the blocks found to positions. + * Retrieves a list of entities of type [T] within the specified range. + * + * @param range The radius around the position to search for entities. + * @param predicate A filter to determine which entities to include in the search. + * @return A list of entities of type [T]. + */ + inline fun entities( + range: Double, + predicate: (T) -> Boolean = { true }, + ): List { + val entities = mutableListOf() + + safeContext.getEntities(pos, range, entities, predicate = predicate) + + return entities + } + + /** + * Searches for blocks within the specified range and step size. + * + * @param range The maximum distance from the position to search for blocks. + * @param step The interval at which to check for blocks. Defaults to 1. + * @param predicate A filter to determine which blocks to include in the search. Defaults to always true. + * @return A map of positions to block states. */ inline fun blocks( range: Int, @@ -68,26 +99,31 @@ class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { ) /** - * Search for blocks in the specified range. - * @param range The range to search in. - * @param step The step to search with. - * @param predicate The predicate to filter the blocks. - * @return A map of the blocks found to positions. + * Searches for blocks within the specified 3D range and step size. + * + * @param range The vector representing the maximum distances from the position to search for blocks. + * @param step The vector representing the intervals at which to check for blocks. + * @param predicate A filter to determine which blocks to include in the search. + * @return A map of positions to block states. */ inline fun blocks( range: Vec3d, step: Vec3i = Vec3i(1, 1, 1), crossinline predicate: (Vec3d, BlockState) -> Boolean = { _, _ -> true } - ): Map = blocks(range, Vec3d(step.x.toDouble(), step.y.toDouble(), step.z.toDouble()), predicate) + ): Map = blocks( + range, + Vec3d(step.x.toDouble(), step.y.toDouble(), step.z.toDouble()), + predicate + ) /** - * Search for blocks in the specified range. + * Searches for blocks within the specified 3D range and step size, with an optional iterator function. * - * @param range The range to search in. - * @param step The step to search with. - * @param predicate The predicate to filter the blocks. - * @param iterator The iterator to call for each block found. - * @return A map of the blocks found to positions. + * @param range The 3D vector representing the maximum distances from the position to search for blocks. + * @param step The 3D vector representing the intervals at which to check for blocks. + * @param predicate A filter to determine which blocks to include in the search. + * @param iterator A function to be called for each block found, allowing additional processing + * @return A map of positions to block states. */ inline fun blocks( range: Vec3d, @@ -107,11 +143,12 @@ class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { } /** - * Search for fluids in the specified range. - * @param range The range to search in. - * @param step The step to search with. - * @param predicate The predicate to filter the fluids. - * @return A map of the fluids found to positions. + * Searches for fluids within the specified range and step size. + * + * @param range The maximum distance from the position to search for fluids. + * @param step The interval at which to check for fluids. + * @param predicate A filter to determine which fluids to include in the search. + * @return A map of positions to fluids. */ inline fun fluids( range: Int, @@ -124,25 +161,31 @@ class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { ) /** - * Search for fluids in the specified range. - * @param range The range to search in. - * @param step The step to search with. - * @param predicate The predicate to filter the fluids. - * @return A map of the fluids found to positions. + * Searches for fluids within the specified 3D range and step size. + * + * @param range The vector representing the maximum distances from the position to search for fluids. + * @param step The vector representing the intervals at which to check for fluids. Defaults to (1, 1, 1). + * @param predicate A filter to determine which fluids to include in the search. + * @return A map of positions to fluids. */ inline fun fluids( range: Vec3d, step: Vec3i = Vec3i(1, 1, 1), crossinline predicate: (Vec3d, FluidState) -> Boolean = { _, _ -> true }, - ): Map = fluids(range, Vec3d(step.x.toDouble(), step.y.toDouble(), step.z.toDouble()), predicate) + ): Map = fluids( + range, + Vec3d(step.x.toDouble(), step.y.toDouble(), step.z.toDouble()), + predicate + ) /** - * Search for fluids in the specified range. - * @param range The range to search in. - * @param step The step to search with. - * @param predicate The predicate to filter the fluids. - * @param iterator The iterator to call for each fluid found. - * @return A map of the fluids found to positions. + * Searches for fluids within the specified 3D range and step size, with an optional iterator function. + * + * @param range The vector representing the maximum distances from the position to search for fluids. + * @param step The vector representing the intervals at which to check for fluids. + * @param predicate A filter to determine which fluids to include in the search. + * @param iterator A function to be called for each fluid found, allowing additional processing. + * @return A map of positions to fluids. */ inline fun fluids( range: Vec3d, @@ -162,5 +205,11 @@ class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { } } +/** + * Initiates a search operation in the world at the specified position using a [SearchContext]. + * + * @param pos The position to start the search from. Defaults to the player's current position. + * @param block The block of code that performs the search using the [SearchContext]. + */ fun SafeContext.search(pos: Vec3d = player.pos, block: SearchContext.() -> Unit) = SearchContext(this, pos).apply(block) diff --git a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt index 2d698eb78..d72feaf21 100644 --- a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt @@ -43,8 +43,6 @@ object WorldUtils { /** * Gets the closest entity of type [T] within a specified range. * - * Because we don't want to troll the CPU speculative execution, we only use the [getFastEntities] function. - * This should not be an issue as the performance of this function is optimized for small distances. * * @param pos The position to search from. * @param range The maximum distance to search for entities. @@ -67,6 +65,7 @@ object WorldUtils { } } + // We use this function to find the closest entity because it is optimized for small distances. getFastEntities(pos, range, null, comparator, predicate) return closest @@ -78,21 +77,16 @@ object WorldUtils { * This function retrieves entities of type [T] within a specified distance from a given position. It efficiently * queries nearby chunks based on the distance and returns a list of matching entities, excluding the player entity. * - * - * Getting all Zombie entities within a certain distance: - * ``` - * val nearbyZombies = getFastEntities(playerPos, 20.0) - * ``` - * - * Getting all hostile entities within a certain distance: + * Examples: + * - Getting all hostile entities within a certain distance: * ``` - * val hostileEntities = getFastEntities(playerPos, 30.0) + * val hostileEntities = mutableListOf() + * getFastEntities(player.pos, 30.0, hostileEntities) * ``` - * This fetches all hostile entities (e.g., Monsters) within a 30-block radius from the player's position. * - * Please note that this implementation is optimized for performance at small distances. For larger distances, it is - * recommended to use the [getEntities] function instead. - * With the time complexity, we can determine that after 64 blocks, the performance of this function will degrade. + * Please note that this implementation is optimized for performance at small distances. + * For larger distances, it is recommended to use the [getEntities] function instead. + * With the time complexity, we can determine that the performance of this function will degrade after 64 blocks. * * @param pos The position to search from. * @param distance The maximum distance to search for entities. From 7b1b9ec391babd31368ed0a96b02a92c7d158be4 Mon Sep 17 00:00:00 2001 From: Constructor Date: Mon, 19 Aug 2024 03:50:35 +0200 Subject: [PATCH 06/11] More descriptive api --- .../lambda/module/modules/debug/BlockTest.kt | 7 +++-- .../kotlin/com/lambda/util/world/Position.kt | 31 +++++++++++++------ .../kotlin/com/lambda/util/world/WorldDsl.kt | 16 +++++----- .../com/lambda/util/world/WorldUtils.kt | 9 ++++-- 4 files changed, 40 insertions(+), 23 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt index 2c4347ee7..77683f383 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt @@ -5,6 +5,7 @@ import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.graphics.renderer.esp.builders.build import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.util.math.VecUtils.blockPos import com.lambda.util.world.search import net.minecraft.block.Blocks import net.minecraft.util.math.Box @@ -33,8 +34,10 @@ object BlockTest : Module( blocks( range = Vec3d(rangeX, rangeY, rangeZ), step = Vec3i(stepX, stepY, stepZ), - ) { _, block -> block.isOf(Blocks.DIAMOND_BLOCK) }.forEach { (pos, _) -> - it.renderer.build(Box.of(pos, 1.0, 1.0, 1.0), filledColor, outlineColor) + ) { _, block -> block.isOf(Blocks.DIAMOND_BLOCK) }.forEach { (pos, state) -> + state.getOutlineShape(world, pos.blockPos).boundingBoxes.forEach { box -> + it.renderer.build(box.offset(pos), filledColor, outlineColor) + } } } } diff --git a/common/src/main/kotlin/com/lambda/util/world/Position.kt b/common/src/main/kotlin/com/lambda/util/world/Position.kt index 8adc75fcd..23d95b258 100644 --- a/common/src/main/kotlin/com/lambda/util/world/Position.kt +++ b/common/src/main/kotlin/com/lambda/util/world/Position.kt @@ -1,5 +1,6 @@ package com.lambda.util.world +import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3d import net.minecraft.util.math.Vec3i @@ -8,10 +9,10 @@ import net.minecraft.util.math.Vec3i * * [ X (26 bits) | Z (26 bits) | Y (12 bits) ] * - * The position is encoded as a 64-bit long where the X and Z coordinates are stored in the most significant 26 bits - * and the Y coordinate is stored in the middle 12 bits. This encoding allows for a maximum world size of - * ±33,554,432 blocks in the X and Z directions and ±2,048 blocks in the Y direction, which is more than - * needed. + * The position is encoded as a 64-bit long where the X and Z coordinates are stored in the 26 most significant bits, + * and the Y coordinate is stored in the 12 least significant bits. + * This encoding allows for a maximum world size of ±33,554,432 blocks + * in the X and Z directions and ±2,048 blocks in the Y direction, which is more than needed. */ typealias FastVector = Long @@ -123,18 +124,28 @@ infix fun FastVector.plus(vec: FastVector): FastVector = fastVectorOf(x + vec.x, infix fun FastVector.plus(vec: Vec3i): FastVector = fastVectorOf(x + vec.x, y + vec.y, z + vec.z) /** - * Encodes the vector as a position. + * Converts a [Vec3i] to a [FastVector]. * @return The encoded position. */ -fun Vec3i.encoded(): FastVector = fastVectorOf(x.toLong(), y.toLong(), z.toLong()) +fun Vec3i.toFastVec(): FastVector = fastVectorOf(x.toLong(), y.toLong(), z.toLong()) /** - * Encodes the vector as a position. + * Converts a [Vec3d] to a [FastVector]. * @return The encoded position. */ -fun Vec3d.encoded(): FastVector = fastVectorOf(x.toLong(), y.toLong(), z.toLong()) +fun Vec3d.toFastVec(): FastVector = fastVectorOf(x.toLong(), y.toLong(), z.toLong()) /** - * Decodes the position into a vector. + * Converts the [FastVector] into a [Vec3d]. */ -fun FastVector.decoded(): Vec3d = Vec3d(x.toDouble(), y.toDouble(), z.toDouble()) +fun FastVector.toVec3d(): Vec3d = Vec3d(x.toDouble(), y.toDouble(), z.toDouble()) + +/** + * Converts the [FastVector] into a [Vec3i]. + */ +fun FastVector.toVec3i(): Vec3i = Vec3i(x, y, z) + +/** + * Converts the [FastVector] into a [BlockPos]. + */ +fun FastVector.toBlockPos(): BlockPos = BlockPos(x, y, z) diff --git a/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt b/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt index f7e687e96..a2fb69902 100644 --- a/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt +++ b/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt @@ -134,12 +134,12 @@ class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { val blocks = mutableMapOf() val transformedPredicate: (FastVector, BlockState, Int) -> Boolean = - { fast, state, _ -> predicate(fast.decoded(), state) } + { fast, state, _ -> predicate(fast.toVec3d(), state) } val transformedIterator: (FastVector, BlockState, Int) -> Unit = - { fast, state, _ -> iterator(fast.decoded(), state) } + { fast, state, _ -> iterator(fast.toVec3d(), state) } - safeContext.searchBlocks(pos.encoded(), range.encoded(), step.encoded(), blocks, transformedPredicate, transformedIterator) - return blocks.mapKeys { it.key.decoded() } + safeContext.searchBlocks(pos.toFastVec(), range.toFastVec(), step.toFastVec(), blocks, transformedPredicate, transformedIterator) + return blocks.mapKeys { it.key.toVec3d() } } /** @@ -196,12 +196,12 @@ class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { val fluids = mutableMapOf() val transformedPredicate: (FastVector, FluidState, Int) -> Boolean = - { fast, state, _ -> predicate(fast.decoded(), state) } + { fast, state, _ -> predicate(fast.toVec3d(), state) } val transformedIterator: (FastVector, FluidState, Int) -> Unit = - { fast, state, _ -> iterator(fast.decoded(), state) } + { fast, state, _ -> iterator(fast.toVec3d(), state) } - safeContext.searchFluids(pos.encoded(), range.encoded(), step.encoded(), fluids, transformedPredicate, transformedIterator) - return fluids.mapKeys { it.key.decoded() } + safeContext.searchFluids(pos.toFastVec(), range.toFastVec(), step.toFastVec(), fluids, transformedPredicate, transformedIterator) + return fluids.mapKeys { it.key.toVec3d() } } } diff --git a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt index d72feaf21..ca823dd2c 100644 --- a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt @@ -40,6 +40,9 @@ import kotlin.math.ceil * @see Medium - GC and Its Effect on Java Performance */ object WorldUtils { + // ToDo: Rename and add explanation + const val MAGIC = 274945015809L + /** * Gets the closest entity of type [T] within a specified range. * @@ -163,7 +166,7 @@ object WorldUtils { inline fun SafeContext.searchBlocks( pos: FastVector, range: FastVector, - step: FastVector = 274945015809L, + step: FastVector = MAGIC, pointer: MutableMap? = null, predicate: (FastVector, BlockState, Int) -> Boolean = { _, _, _ -> true }, iterator: (FastVector, BlockState, Int) -> Unit = { _, _, _ -> }, @@ -192,7 +195,7 @@ object WorldUtils { inline fun SafeContext.searchFluids( pos: FastVector, range: FastVector, - step: FastVector = 274945015809L, + step: FastVector = MAGIC, pointer: MutableMap? = null, predicate: (FastVector, FluidState, Int) -> Boolean = { _, _, _ -> true }, iterator: (FastVector, FluidState, Int) -> Unit = { _, _, _ -> }, @@ -219,7 +222,7 @@ object WorldUtils { inline fun iteratePositions( pos: FastVector, range: FastVector, - step: FastVector = 274945015809L, + step: FastVector = MAGIC, iterator: (FastVector, Int) -> Unit = { _, _ -> }, ) { var index = 0 From 12f98969b9c536b58a8cce81e0e20ce8bdbe2158 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Mon, 19 Aug 2024 15:25:37 -0400 Subject: [PATCH 07/11] refactor!: vec3d -> vec3i + argument ref --- .../com/lambda/config/groups/Targeting.kt | 28 +--- .../lambda/core/annotations/InternalApi.kt | 10 ++ .../module/modules/combat/CrystalAura.kt | 6 +- .../lambda/module/modules/debug/BlockTest.kt | 19 ++- .../lambda/module/modules/debug/EntityTest.kt | 22 --- .../module/modules/movement/EntityControl.kt | 38 ++--- .../lambda/module/modules/movement/Speed.kt | 30 ++-- .../com/lambda/util/combat/Explosion.kt | 6 +- .../kotlin/com/lambda/util/math/VecUtils.kt | 12 ++ .../kotlin/com/lambda/util/world/Position.kt | 120 +++++++++++--- .../kotlin/com/lambda/util/world/WorldDsl.kt | 149 +++++++++--------- .../com/lambda/util/world/WorldUtils.kt | 73 +++------ 12 files changed, 278 insertions(+), 235 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/core/annotations/InternalApi.kt delete mode 100644 common/src/main/kotlin/com/lambda/module/modules/debug/EntityTest.kt diff --git a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt index aef28e9bf..95989ced8 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt @@ -7,7 +7,10 @@ import com.lambda.interaction.rotation.Rotation.Companion.rotation import com.lambda.interaction.rotation.Rotation.Companion.rotationTo import com.lambda.threading.runSafe import com.lambda.util.math.VecUtils.distSq +import com.lambda.util.world.SearchContext import com.lambda.util.world.WorldUtils.getFastEntities +import com.lambda.util.world.search +import com.lambda.util.world.toFastVec import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.entity.LivingEntity import net.minecraft.entity.mob.MobEntity @@ -31,16 +34,6 @@ abstract class Targeting( override val invisible by c.setting("Invisible", true) { vis() } override val dead by c.setting("Dead", false) { vis() } - fun getEntities(): List = - mutableListOf().apply { - runSafe { - getFastEntities( - player.pos, targetingRange, this@apply, - predicate = { entity -> validate(player, entity) } - ) - } - } - open fun validate(player: ClientPlayerEntity, entity: LivingEntity) = when { !players && entity.isPlayer -> false !animals && entity is PassiveEntity -> false @@ -66,21 +59,14 @@ abstract class Targeting( fun getTarget(): LivingEntity? = runSafe { var best: LivingEntity? = null - var bestFactor = Double.MAX_VALUE - - val comparator = { entity: LivingEntity, _: Int -> - val factor = priority.factor(this, entity) - if (factor < bestFactor) { - best = entity - bestFactor = factor - } - } val predicate = { entity: LivingEntity -> validate(player, entity) } - getFastEntities(player.pos, targetingRange, null, comparator, predicate) + search { + best = minEntityBy(targetingRange, predicate, priority.factor) + } return@runSafe best } @@ -97,4 +83,4 @@ abstract class Targeting( HEALTH({ it.health.toDouble() }), FOV({ player.rotation dist player.eyePos.rotationTo(it.pos) }) } -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/com/lambda/core/annotations/InternalApi.kt b/common/src/main/kotlin/com/lambda/core/annotations/InternalApi.kt new file mode 100644 index 000000000..9cd70a634 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/core/annotations/InternalApi.kt @@ -0,0 +1,10 @@ +package com.lambda.core.annotations + +import kotlin.annotation.AnnotationTarget.* + +@RequiresOptIn( + level = RequiresOptIn.Level.WARNING, + message = "Only use if you know what you are doing, no support will be provided whatsoever, use at your own risk", +) +@Target(CLASS, FUNCTION, PROPERTY, ANNOTATION_CLASS, CONSTRUCTOR, PROPERTY_SETTER, PROPERTY_GETTER, TYPEALIAS) +internal annotation class InternalApi diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index b7401ca1b..83c2b6a99 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -7,7 +7,7 @@ import com.lambda.event.listener.SafeListener.Companion.concurrentListener import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.util.world.WorldUtils.getClosestEntity +import com.lambda.util.world.search import net.minecraft.entity.LivingEntity import net.minecraft.util.Hand @@ -56,9 +56,5 @@ object CrystalAura : Module( /*listener { event -> event.lookAtEntity(rotation, interac, getClosestEntity(player.eyePos, placeRange) ?: return@listener) }*/ - - listener { - getClosestEntity(player.eyePos, 64.0) - } } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt index 77683f383..9fa01c807 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt @@ -5,6 +5,7 @@ import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.graphics.renderer.esp.builders.build import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.util.BlockUtils.blockPos import com.lambda.util.math.VecUtils.blockPos import com.lambda.util.world.search import net.minecraft.block.Blocks @@ -18,13 +19,19 @@ object BlockTest : Module( description = "BlockTest", defaultTags = setOf(ModuleTag.DEBUG), ) { - private val rangeX by setting("Range X", 5.0, 0.1..7.0, 0.1, "Range X") - private val rangeY by setting("Range Y", 5.0, 0.1..7.0, 0.1, "Range Y") - private val rangeZ by setting("Range Z", 5.0, 0.1..7.0, 0.1, "Range Z") + private val rangeX by setting("Range X", 5, 1..7, 1, "Range X") + private val rangeY by setting("Range Y", 5, 1..7, 1, "Range Y") + private val rangeZ by setting("Range Z", 5, 1..7, 1, "Range Z") private val stepX by setting("Step X", 1, 1..7, 1, "Step X") private val stepY by setting("Step Y", 1, 1..7, 1, "Step Y") private val stepZ by setting("Step Z", 1, 1..7, 1, "Step Z") + private val range: Vec3i + get() = Vec3i(rangeX, rangeY, rangeZ) + + private val step: Vec3i + get() = Vec3i(stepX, stepY, stepZ) + private val filledColor = Color(100, 150, 255, 128) private val outlineColor = Color(100, 150, 255, 51) @@ -32,10 +39,10 @@ object BlockTest : Module( listener { search { blocks( - range = Vec3d(rangeX, rangeY, rangeZ), - step = Vec3i(stepX, stepY, stepZ), + range, + step ) { _, block -> block.isOf(Blocks.DIAMOND_BLOCK) }.forEach { (pos, state) -> - state.getOutlineShape(world, pos.blockPos).boundingBoxes.forEach { box -> + state.getOutlineShape(world, pos).boundingBoxes.forEach { box -> it.renderer.build(box.offset(pos), filledColor, outlineColor) } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/EntityTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/EntityTest.kt deleted file mode 100644 index 9db4a1aba..000000000 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/EntityTest.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.lambda.module.modules.debug - -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listener -import com.lambda.module.Module -import com.lambda.module.tag.ModuleTag -import com.lambda.util.world.WorldUtils.getClosestEntity -import net.minecraft.entity.Entity - -object EntityTest : Module( - name = "EntityTest", - description = "Test entity", - defaultTags = setOf(ModuleTag.DEBUG) -) { - init { - listener { - repeat(10000) { - getClosestEntity(player.eyePos, 7.0) - } - } - } -} diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/EntityControl.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/EntityControl.kt index f67665104..2b4958156 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/EntityControl.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/EntityControl.kt @@ -1,11 +1,13 @@ package com.lambda.module.modules.movement +import com.lambda.context.SafeContext import com.lambda.event.events.PacketEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.world.WorldUtils.getEntities +import com.lambda.util.world.search import net.minecraft.entity.passive.AbstractHorseEntity import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket @@ -15,39 +17,21 @@ object EntityControl : Module( description = "Control mountable entities", defaultTags = setOf(ModuleTag.MOVEMENT) ) { - private val page by setting("Page", Page.GENERAL) - - /* General */ - private val forceMount by setting("Force Mount", true, description = "Attempts to force mount chested entities.", visibility = { page == Page.GENERAL }).apply { + private val forceMount by setting("Force Mount", true, description = "Attempts to force mount chested entities.").apply { onValueChange { _, _ -> - horses.forEach { horse -> horse.updateSaddle() } + resetHorseFlags() } } - /* Movement */ - private val speed by setting("Entity Speed", 2.0, 0.1..10.0, 0.1, description = "Speed for entities.", visibility = { page == Page.MOVEMENT }) - - private enum class Page { - GENERAL, MOVEMENT - } - - private val horses = mutableListOf() - init { listener { if (forceMount) { - getEntities(player.pos, 8.0, horses, { horse, _ -> horse.setHorseFlag(4, true) }) + search { + nearbyEntities(8.0) { it.setHorseFlag(4, true) } + } } } - /*listener { - if (!player.isRiding) return@listener - - // We can do this because the player movement depends on the entity movement - player.vehicle?.motionX = speed - player.vehicle?.motionZ = speed - }*/ - listener { event -> if (!forceMount) return@listener if (event.packet !is PlayerInteractEntityC2SPacket) return@listener @@ -60,7 +44,13 @@ object EntityControl : Module( } onDisable { - horses.clear() + resetHorseFlags() + } + } + + fun SafeContext.resetHorseFlags() { + search { + entities(500) { it.updateSaddle() } } } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt index ea49516a9..17a63881f 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt @@ -26,6 +26,7 @@ import com.lambda.util.player.MovementUtils.roundedStrafing import com.lambda.util.player.MovementUtils.setSpeed import com.lambda.util.primitives.extension.contains import com.lambda.util.world.WorldUtils.getFastEntities +import com.lambda.util.world.search import net.minecraft.entity.LivingEntity import net.minecraft.entity.decoration.ArmorStandEntity import net.minecraft.entity.vehicle.BoatEntity @@ -134,22 +135,21 @@ object Speed : Module( var boostAmount = 0.0 - getFastEntities( - player.pos, 3.0, - predicate = { player.boundingBox.expand(1.0) in it.boundingBox && it !is ArmorStandEntity }, - iterator = { e, _ -> - val colliding = player.boundingBox in e.boundingBox - val multiplier = if (colliding) grimCollideMultiplier else 1.0 - boostAmount += 0.08 * grimEntityBoost * multiplier + search { + nearbyEntities(3.0, + predicate = { player.boundingBox.expand(1.0) in it.boundingBox && it !is ArmorStandEntity } + ) { entity -> + val colliding = player.boundingBox in entity.boundingBox + val multiplier = if (colliding) grimCollideMultiplier else 1.0 + boostAmount += 0.08 * grimEntityBoost * multiplier + } + + if (grimBoatBoost > 0.0) { + nearbyEntities( + 4.0, + predicate = { player.boundingBox in it.boundingBox.expand(0.01) }, + ) { _ -> boostAmount += grimBoatBoost } } - ) - - if (grimBoatBoost > 0.0) { - getFastEntities( - player.pos, 4.0, - predicate = { player.boundingBox in it.boundingBox.expand(0.01) }, - iterator = { _, _ -> boostAmount += grimBoatBoost } - ) } addSpeed(boostAmount) diff --git a/common/src/main/kotlin/com/lambda/util/combat/Explosion.kt b/common/src/main/kotlin/com/lambda/util/combat/Explosion.kt index ac559c39c..854b76136 100644 --- a/common/src/main/kotlin/com/lambda/util/combat/Explosion.kt +++ b/common/src/main/kotlin/com/lambda/util/combat/Explosion.kt @@ -1,11 +1,15 @@ +@file:OptIn(InternalApi::class) + package com.lambda.util.combat import com.lambda.context.SafeContext +import com.lambda.core.annotations.InternalApi import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.fluidState import com.lambda.util.math.VecUtils.minus import com.lambda.util.math.VecUtils.times import com.lambda.util.world.WorldUtils.getFastEntities +import com.lambda.util.world.toFastVec import net.minecraft.enchantment.ProtectionEnchantment import net.minecraft.entity.LivingEntity import net.minecraft.util.math.BlockPos @@ -51,7 +55,7 @@ object Explosion { */ fun SafeContext.explosionVelocity(explosion: Explosion): Map { val ref = ArrayList() - getFastEntities(explosion.position, explosion.power * 2.0, ref) + getFastEntities(explosion.position.toFastVec(), explosion.power * 2.0, ref) return ref.associateWith { entity -> explosionVelocity(entity, explosion) } } diff --git a/common/src/main/kotlin/com/lambda/util/math/VecUtils.kt b/common/src/main/kotlin/com/lambda/util/math/VecUtils.kt index 21d3626fc..bc22e2911 100644 --- a/common/src/main/kotlin/com/lambda/util/math/VecUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/math/VecUtils.kt @@ -1,6 +1,11 @@ package com.lambda.util.math import com.lambda.util.math.MathUtils.sq +import com.lambda.util.world.FastVector +import com.lambda.util.world.x +import com.lambda.util.world.y +import com.lambda.util.world.z +import net.minecraft.entity.Entity import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3d import net.minecraft.util.math.Vec3i @@ -11,6 +16,9 @@ object VecUtils { val Vec3d.blockPos: BlockPos get() = BlockPos(x.roundToInt(), y.roundToInt(), z.roundToInt()) + val Vec3i.asVec3d: Vec3d + get() = Vec3d(x.toDouble(), y.toDouble(), z.toDouble()) + infix fun Vec3d.dist(other: Vec3d): Double = this.distanceTo(other) infix fun Vec3d.distSq(other: Vec3d): Double = this.squaredDistanceTo(other) @@ -21,6 +29,10 @@ object VecUtils { infix fun Vec3i.distSq(other: Vec3i): Int = (this.x - other.x).sq + (this.y - other.y).sq + (this.z - other.z).sq + infix fun Entity.distSq(other: Vec3d): Double = this.pos distSq other + + infix fun Entity.distSq(other: Vec3i): Int = this.blockPos distSq other + infix operator fun Vec3d.plus(other: Vec3d): Vec3d = this.add(other) infix operator fun Vec3d.minus(other: Vec3d): Vec3d = this.subtract(other) diff --git a/common/src/main/kotlin/com/lambda/util/world/Position.kt b/common/src/main/kotlin/com/lambda/util/world/Position.kt index 23d95b258..794ba630c 100644 --- a/common/src/main/kotlin/com/lambda/util/world/Position.kt +++ b/common/src/main/kotlin/com/lambda/util/world/Position.kt @@ -3,6 +3,7 @@ package com.lambda.util.world import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3d import net.minecraft.util.math.Vec3i +import kotlin.math.sqrt /** * Represents a position in the world encoded as a long. @@ -34,11 +35,6 @@ internal const val MAX_X = (1L shl X_BITS - 1) - 1L internal const val MAX_Z = (1L shl Z_BITS - 1) - 1L internal const val MAX_Y = (1L shl Y_BITS - 1) - 1L -internal fun Long.bitSetTo(value: Long, position: Int, length: Int): Long { - val mask = (1L shl length) - 1L - return this and (mask shl position).inv() or (value and mask shl position) -} - /** * Creates a new position from the given coordinates. */ @@ -85,32 +81,32 @@ val FastVector.y: Int /** * Sets the X coordinate of the position. */ -infix fun FastVector.withX(x: Int): FastVector = bitSetTo(x.toLong(), X_SHIFT, X_BITS) +infix fun FastVector.setX(x: Int): FastVector = bitSetTo(x.toLong(), X_SHIFT, X_BITS) /** - * Sets the Z coordinate of the position. + * Sets the Y coordinate of the position. */ -infix fun FastVector.withZ(z: Int): FastVector = bitSetTo(z.toLong(), Z_SHIFT, Z_BITS) +infix fun FastVector.setY(y: Int): FastVector = bitSetTo(y.toLong(), 0, Y_BITS) /** - * Sets the Y coordinate of the position. + * Sets the Z coordinate of the position. */ -infix fun FastVector.withY(y: Int): FastVector = bitSetTo(y.toLong(), 0, Y_BITS) +infix fun FastVector.setZ(z: Int): FastVector = bitSetTo(z.toLong(), Z_SHIFT, Z_BITS) /** * Adds the given value to the X coordinate. */ -infix fun FastVector.addX(value: Int): FastVector = withX(x + value) +infix fun FastVector.addX(value: Int): FastVector = setX(x + value) /** - * Adds the given value to the Z coordinate. + * Adds the given value to the Y coordinate. */ -infix fun FastVector.addZ(value: Int): FastVector = withZ(z + value) +infix fun FastVector.addY(value: Int): FastVector = setY(y + value) /** - * Adds the given value to the Y coordinate. + * Adds the given value to the Z coordinate. */ -infix fun FastVector.addY(value: Int): FastVector = withY(y + value) +infix fun FastVector.addZ(value: Int): FastVector = setZ(z + value) /** * Adds the given vector to the position. @@ -123,6 +119,89 @@ infix fun FastVector.plus(vec: FastVector): FastVector = fastVectorOf(x + vec.x, */ infix fun FastVector.plus(vec: Vec3i): FastVector = fastVectorOf(x + vec.x, y + vec.y, z + vec.z) +/** + * Adds the given vector to the position. + * @return The new position. + */ +infix fun FastVector.plus(vec: Vec3d): FastVector = fastVectorOf(x + vec.x.toLong(), y + vec.y.toLong(), z + vec.z.toLong()) + +/** + * Subtracts the given vector from the position. + */ +infix fun FastVector.minus(vec: FastVector): FastVector = fastVectorOf(x - vec.x, y - vec.y, z - vec.z) + +/** + * Subtracts the given vector from the position. + * @return The new position. + */ +infix fun FastVector.minus(vec: Vec3i): FastVector = fastVectorOf(x - vec.x, y - vec.y, z - vec.z) + +/** + * Subtracts the given vector from the position. + * @return The new position. + */ +infix fun FastVector.minus(vec: Vec3d): FastVector = fastVectorOf(x - vec.x.toLong(), y - vec.y.toLong(), z - vec.z.toLong()) + +/** + * Multiplies the position by the given scalar. + */ +infix fun FastVector.times(scalar: Int): FastVector = fastVectorOf(x * scalar, y * scalar, z * scalar) + +/** + * Multiplies the position by the given scalar. + */ +infix fun FastVector.times(scalar: Double): FastVector = fastVectorOf((x * scalar).toLong(), (y * scalar).toLong(), (z * scalar).toLong()) + +/** + * Divides the position by the given scalar. + */ +infix fun FastVector.div(scalar: Int): FastVector = fastVectorOf(x / scalar, y / scalar, z / scalar) + +/** + * Divides the position by the given scalar. + */ +infix fun FastVector.div(scalar: Double): FastVector = fastVectorOf((x / scalar).toLong(), (y / scalar).toLong(), (z / scalar).toLong()) + +/** + * Modulo the position by the given scalar. + */ +infix fun FastVector.mod(scalar: Int): FastVector = fastVectorOf(x % scalar, y % scalar, z % scalar) + +/** + * Modulo the position by the given scalar. + */ +infix fun FastVector.mod(scalar: Double): FastVector = fastVectorOf((x % scalar).toLong(), (y % scalar).toLong(), (z % scalar).toLong()) + +/** + * Returns the squared distance between this position and the other. + */ +infix fun FastVector.distSq(other: FastVector): Double { + val dx = x - other.x + val dy = y - other.y + val dz = z - other.z + return (dx * dx + dy * dy + dz * dz).toDouble() +} + +/** + * Returns the squared distance between this position and the Vec3i. + */ +infix fun FastVector.distSq(other: Vec3i): Double { + val dx = x - other.x + val dy = y - other.y + val dz = z - other.z + return (dx * dx + dy * dy + dz * dz).toDouble() +} + +/** + * Returns the squared distance between this position and the Vec3d. + */ +infix fun FastVector.distSq(other: Vec3d): Double { + val dx = x - other.x.toLong() + val dy = y - other.y.toLong() + val dz = z - other.z.toLong() + return (dx * dx + dy * dy + dz * dz).toDouble() +} + /** * Converts a [Vec3i] to a [FastVector]. * @return The encoded position. @@ -141,11 +220,14 @@ fun Vec3d.toFastVec(): FastVector = fastVectorOf(x.toLong(), y.toLong(), z.toLon fun FastVector.toVec3d(): Vec3d = Vec3d(x.toDouble(), y.toDouble(), z.toDouble()) /** - * Converts the [FastVector] into a [Vec3i]. + * Converts the [FastVector] into a [BlockPos]. */ -fun FastVector.toVec3i(): Vec3i = Vec3i(x, y, z) +fun FastVector.toBlockPos(): BlockPos = BlockPos(x, y, z) /** - * Converts the [FastVector] into a [BlockPos]. + * Sets n bits to a value at a given position. */ -fun FastVector.toBlockPos(): BlockPos = BlockPos(x, y, z) +internal fun Long.bitSetTo(value: Long, position: Int, length: Int): Long { + val mask = (1L shl length) - 1L + return this and (mask shl position).inv() or (value and mask shl position) +} diff --git a/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt b/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt index a2fb69902..feebad7d0 100644 --- a/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt +++ b/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt @@ -1,8 +1,11 @@ +@file:OptIn(InternalApi::class) + package com.lambda.util.world import com.lambda.Lambda.LOG import com.lambda.context.SafeContext -import com.lambda.util.world.WorldUtils.getClosestEntity +import com.lambda.core.annotations.InternalApi +import com.lambda.util.math.VecUtils.distSq import com.lambda.util.world.WorldUtils.getEntities import com.lambda.util.world.WorldUtils.getFastEntities import com.lambda.util.world.WorldUtils.searchBlocks @@ -11,7 +14,7 @@ import net.minecraft.block.BlockState import net.minecraft.entity.Entity import net.minecraft.fluid.Fluid import net.minecraft.fluid.FluidState -import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3i @DslMarker @@ -26,7 +29,9 @@ annotation class WorldDsl * @property pos The position from which searches are conducted. */ @WorldDsl -class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { +class SearchContext(val safeContext: SafeContext, val pos: BlockPos) { + val fastVector: FastVector = pos.toFastVec() + /** * Finds the closest entity of type [T] to the specified position within a given range. * @@ -37,7 +42,36 @@ class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { inline fun closestEntity( range: Double, predicate: (T) -> Boolean = { true }, - ): T? = safeContext.getClosestEntity(pos, range, predicate) + ): T? = minEntityBy(range, predicate) { entity -> pos distSq entity.pos } + + /** + * Returns the entity that has the smallest value based on the given comparator. + * If multiple entities have the same value, the first one found will be returned. + * + * @param range The maximum distance from the position to search for entities. + * @param comparator A function that compares two entities and returns a value. + * @param predicate A filter to determine which entities to include in the search. + * @return The entity with the smallest value based on the comparator, or null if no entity is found. + */ + inline fun minEntityBy( + range: Double, + predicate: (T) -> Boolean = { true }, + comparator: SafeContext.(T) -> Double, + ): T? { + var min: T? = null + var minValue = Double.MAX_VALUE + + safeContext.getFastEntities(fastVector, range, predicate = predicate) { entity, _ -> + val value = comparator(safeContext, entity) + + if (value < minValue) { + min = entity + minValue = value + } + } + + return min + } /** * Retrieves a list of nearby entities of type [T] within the specified range. @@ -49,15 +83,16 @@ class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { inline fun nearbyEntities( range: Double, predicate: (T) -> Boolean = { true }, + iterator: (T) -> Unit = { _ -> }, ): List { - val entities = mutableListOf() - if (range >= 64) { LOG.warn("Searching nearby entities with a range of $range may be slow. " + "Consider reducing the range to improve performance or using a linear search instead.") } - safeContext.getFastEntities(pos, range, entities, predicate = predicate) + val entities = mutableListOf() + + safeContext.getFastEntities(fastVector, range, entities, predicate) { entity, _ -> iterator(entity) } return entities } @@ -70,12 +105,13 @@ class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { * @return A list of entities of type [T]. */ inline fun entities( - range: Double, + range: Int, predicate: (T) -> Boolean = { true }, + iterator: (T) -> Unit = { _ -> }, ): List { val entities = mutableListOf() - safeContext.getEntities(pos, range, entities, predicate = predicate) + safeContext.getEntities(fastVector, range.toDouble(), entities, predicate) { entity, _ -> iterator(entity) } return entities } @@ -91,29 +127,12 @@ class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { inline fun blocks( range: Int, step: Int = 1, - crossinline predicate: (Vec3d, BlockState) -> Boolean = { _, _ -> true } - ): Map = blocks( - Vec3d(range.toDouble(), range.toDouble(), range.toDouble()), - Vec3d(step.toDouble(), step.toDouble(), step.toDouble()), - predicate - ) - - /** - * Searches for blocks within the specified 3D range and step size. - * - * @param range The vector representing the maximum distances from the position to search for blocks. - * @param step The vector representing the intervals at which to check for blocks. - * @param predicate A filter to determine which blocks to include in the search. - * @return A map of positions to block states. - */ - inline fun blocks( - range: Vec3d, - step: Vec3i = Vec3i(1, 1, 1), - crossinline predicate: (Vec3d, BlockState) -> Boolean = { _, _ -> true } - ): Map = blocks( - range, - Vec3d(step.x.toDouble(), step.y.toDouble(), step.z.toDouble()), - predicate + crossinline predicate: (BlockPos, BlockState) -> Boolean = { _, _ -> true }, + crossinline iterator: (BlockPos, BlockState) -> Unit = { _, _ -> }, + ): Map = blocks( + BlockPos(range, range, range), + BlockPos(step, step, step), + predicate, iterator, ) /** @@ -126,20 +145,21 @@ class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { * @return A map of positions to block states. */ inline fun blocks( - range: Vec3d, - step: Vec3d = Vec3d(1.0, 1.0, 1.0), - crossinline predicate: (Vec3d, BlockState) -> Boolean = { _, _ -> true }, - crossinline iterator: (Vec3d, BlockState) -> Unit = { _, _ -> }, - ): Map { + range: Vec3i, + step: Vec3i = Vec3i(1, 1, 1), + crossinline predicate: (BlockPos, BlockState) -> Boolean = { _, _ -> true }, + crossinline iterator: (BlockPos, BlockState) -> Unit = { _, _ -> }, + ): Map { val blocks = mutableMapOf() val transformedPredicate: (FastVector, BlockState, Int) -> Boolean = - { fast, state, _ -> predicate(fast.toVec3d(), state) } + { fast, state, _ -> predicate(fast.toBlockPos(), state) } val transformedIterator: (FastVector, BlockState, Int) -> Unit = - { fast, state, _ -> iterator(fast.toVec3d(), state) } + { fast, state, _ -> iterator(fast.toBlockPos(), state) } - safeContext.searchBlocks(pos.toFastVec(), range.toFastVec(), step.toFastVec(), blocks, transformedPredicate, transformedIterator) - return blocks.mapKeys { it.key.toVec3d() } + safeContext.searchBlocks(fastVector, range.toFastVec(), step.toFastVec(), blocks, transformedPredicate, transformedIterator) + + return blocks.mapKeys { it.key.toBlockPos() } } /** @@ -153,29 +173,12 @@ class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { inline fun fluids( range: Int, step: Int = 1, - crossinline predicate: (Vec3d, FluidState) -> Boolean = { _, _ -> true }, - ): Map = fluids( - Vec3d(range.toDouble(), range.toDouble(), range.toDouble()), - Vec3d(step.toDouble(), step.toDouble(), step.toDouble()), - predicate - ) - - /** - * Searches for fluids within the specified 3D range and step size. - * - * @param range The vector representing the maximum distances from the position to search for fluids. - * @param step The vector representing the intervals at which to check for fluids. Defaults to (1, 1, 1). - * @param predicate A filter to determine which fluids to include in the search. - * @return A map of positions to fluids. - */ - inline fun fluids( - range: Vec3d, - step: Vec3i = Vec3i(1, 1, 1), - crossinline predicate: (Vec3d, FluidState) -> Boolean = { _, _ -> true }, - ): Map = fluids( - range, - Vec3d(step.x.toDouble(), step.y.toDouble(), step.z.toDouble()), - predicate + crossinline predicate: (BlockPos, FluidState) -> Boolean = { _, _ -> true }, + crossinline iterator: (BlockPos, FluidState) -> Unit = { _, _ -> }, + ): Map = fluids( + BlockPos(range, range, range), + BlockPos(step, step, step), + predicate, iterator, ) /** @@ -188,20 +191,20 @@ class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { * @return A map of positions to fluids. */ inline fun fluids( - range: Vec3d, - step: Vec3d = Vec3d(1.0, 1.0, 1.0), - crossinline predicate: (Vec3d, FluidState) -> Boolean = { _, _ -> true }, - crossinline iterator: (Vec3d, FluidState) -> Unit = { _, _ -> }, - ): Map { + range: Vec3i, + step: Vec3i = Vec3i(1, 1, 1), + crossinline predicate: (BlockPos, FluidState) -> Boolean = { _, _ -> true }, + crossinline iterator: (BlockPos, FluidState) -> Unit = { _, _ -> }, + ): Map { val fluids = mutableMapOf() val transformedPredicate: (FastVector, FluidState, Int) -> Boolean = - { fast, state, _ -> predicate(fast.toVec3d(), state) } + { fast, state, _ -> predicate(fast.toBlockPos(), state) } val transformedIterator: (FastVector, FluidState, Int) -> Unit = - { fast, state, _ -> iterator(fast.toVec3d(), state) } + { fast, state, _ -> iterator(fast.toBlockPos(), state) } safeContext.searchFluids(pos.toFastVec(), range.toFastVec(), step.toFastVec(), fluids, transformedPredicate, transformedIterator) - return fluids.mapKeys { it.key.toVec3d() } + return fluids.mapKeys { it.key.toBlockPos() } } } @@ -211,5 +214,5 @@ class SearchContext(val safeContext: SafeContext, val pos: Vec3d) { * @param pos The position to start the search from. Defaults to the player's current position. * @param block The block of code that performs the search using the [SearchContext]. */ -fun SafeContext.search(pos: Vec3d = player.pos, block: SearchContext.() -> Unit) = +inline fun SafeContext.search(pos: BlockPos = player.blockPos, block: SearchContext.() -> Unit) = SearchContext(this, pos).apply(block) diff --git a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt index ca823dd2c..ae45313b4 100644 --- a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt @@ -1,6 +1,7 @@ package com.lambda.util.world import com.lambda.context.SafeContext +import com.lambda.core.annotations.InternalApi import com.lambda.util.collections.filterPointer import com.lambda.util.primitives.extension.getBlockState import com.lambda.util.primitives.extension.getFluidState @@ -9,7 +10,6 @@ import net.minecraft.entity.Entity import net.minecraft.fluid.Fluid import net.minecraft.fluid.FluidState import net.minecraft.util.math.ChunkSectionPos -import net.minecraft.util.math.Vec3d import kotlin.math.ceil /** @@ -39,40 +39,13 @@ import kotlin.math.ceil * @see IBM - Garbage Collection Impacts on Java Performance * @see Medium - GC and Its Effect on Java Performance */ +@InternalApi object WorldUtils { - // ToDo: Rename and add explanation - const val MAGIC = 274945015809L - /** - * Gets the closest entity of type [T] within a specified range. - * - * - * @param pos The position to search from. - * @param range The maximum distance to search for entities. - * @param predicate Predicate to filter entities. - * @return The first entity of type [T] that is closest to the position within the specified range. + * A magic vector that can be used to represent a single block. + * It is the same as `fastVectorOf(1, 1, 1)`. */ - inline fun SafeContext.getClosestEntity( - pos: Vec3d, - range: Double, - predicate: (T) -> Boolean = { true }, - ): T? { - var closest: T? = null - var closestDistance = Double.MAX_VALUE - - val comparator = { entity: T, _: Int -> - val distance = pos.squaredDistanceTo(entity.pos) - if (distance < closestDistance) { - closest = entity - closestDistance = distance - } - } - - // We use this function to find the closest entity because it is optimized for small distances. - getFastEntities(pos, range, null, comparator, predicate) - - return closest - } + const val MAGICVECTOR = 274945015809L /** * Gets all entities of type [T] within a specified distance from a position. @@ -94,20 +67,20 @@ object WorldUtils { * @param pos The position to search from. * @param distance The maximum distance to search for entities. * @param pointer The mutable list to store the entities in. - * @param iterator Iterator to perform operations on each entity. + * @param iterator Iterator to perform operations on each entity. The second parameter is the index of the iteration. * @param predicate Predicate to filter entities. */ inline fun SafeContext.getFastEntities( - pos: Vec3d, + pos: FastVector, distance: Double, pointer: MutableList? = null, - iterator: (T, Int) -> Unit = { _, _ -> }, predicate: (T) -> Boolean = { true }, + iterator: (T, Int) -> Unit = { _, _ -> }, ) { - val chunks = ceil(distance / 16).toInt() - val sectionX = pos.x.toInt() shr 4 - val sectionY = pos.y.toInt() shr 4 - val sectionZ = pos.z.toInt() shr 4 + val chunks = ceil(distance / 16.0).toInt() + val sectionX = pos.x shr 4 + val sectionY = pos.y shr 4 + val sectionZ = pos.z shr 4 // Here we iterate over all sections within the specified distance and add all entities of type [T] to the list. // We do not have to worry about performance here, as the number of sections is very limited. @@ -122,7 +95,7 @@ object WorldUtils { section.collection.filterPointer(pointer, iterator) { entity -> entity != player && - entity.squaredDistanceTo(pos) <= distance * distance && + pos distSq entity.pos <= distance * distance && predicate(entity) } } @@ -136,20 +109,22 @@ object WorldUtils { * This function retrieves entities of type [T] within a specified distance from a given position. Unlike * [getFastEntities], it traverses all entities in the world to find matches, while also excluding the player entity. * + * @param pos The block position to search from. + * @param distance The maximum distance to search for entities. * @param pointer The mutable list to store the entities in. - * @param iterator Iterator to perform operations on each entity. + * @param iterator Iterator to perform operations on each entity. The second parameter is the index of the iteration. * @param predicate Predicate to filter entities. */ inline fun SafeContext.getEntities( - pos: Vec3d, + pos: FastVector, distance: Double, pointer: MutableList? = null, - iterator: (T, Int) -> Unit = { _, _ -> }, predicate: (T) -> Boolean = { true }, + iterator: (T, Int) -> Unit = { _, _ -> }, ) { world.entities.filterPointer(pointer, iterator) { entity -> entity != player && - entity.squaredDistanceTo(pos) <= distance * distance && + pos distSq entity.pos <= distance * distance && predicate(entity) } } @@ -165,8 +140,8 @@ object WorldUtils { */ inline fun SafeContext.searchBlocks( pos: FastVector, - range: FastVector, - step: FastVector = MAGIC, + range: FastVector = MAGICVECTOR times 7, + step: FastVector = MAGICVECTOR, pointer: MutableMap? = null, predicate: (FastVector, BlockState, Int) -> Boolean = { _, _, _ -> true }, iterator: (FastVector, BlockState, Int) -> Unit = { _, _, _ -> }, @@ -194,8 +169,8 @@ object WorldUtils { */ inline fun SafeContext.searchFluids( pos: FastVector, - range: FastVector, - step: FastVector = MAGIC, + range: FastVector = MAGICVECTOR times 7, + step: FastVector = MAGICVECTOR, pointer: MutableMap? = null, predicate: (FastVector, FluidState, Int) -> Boolean = { _, _, _ -> true }, iterator: (FastVector, FluidState, Int) -> Unit = { _, _, _ -> }, @@ -222,7 +197,7 @@ object WorldUtils { inline fun iteratePositions( pos: FastVector, range: FastVector, - step: FastVector = MAGIC, + step: FastVector, iterator: (FastVector, Int) -> Unit = { _, _ -> }, ) { var index = 0 From 7f2465ff0f15752626e690443305290a9bffba08 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Mon, 19 Aug 2024 17:56:26 -0400 Subject: [PATCH 08/11] refactor!: independent world dsl --- .../com/lambda/config/groups/Targeting.kt | 17 +- .../lambda/core/annotations/InternalApi.kt | 1 - .../module/modules/combat/CrystalAura.kt | 3 - .../lambda/module/modules/debug/BlockTest.kt | 19 +- .../lambda/module/modules/debug/RenderTest.kt | 13 +- .../module/modules/movement/EntityControl.kt | 13 +- .../lambda/module/modules/movement/Speed.kt | 30 +-- .../com/lambda/util/collections/Extensions.kt | 34 ++- .../com/lambda/util/combat/Explosion.kt | 4 +- .../kotlin/com/lambda/util/world/BlockDsl.kt | 141 +++++++++++ .../kotlin/com/lambda/util/world/EntityDsl.kt | 143 ++++++++++++ .../kotlin/com/lambda/util/world/FluidDsl.kt | 146 ++++++++++++ .../kotlin/com/lambda/util/world/WorldDsl.kt | 218 ------------------ .../com/lambda/util/world/WorldUtils.kt | 87 ++++--- 14 files changed, 567 insertions(+), 302 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/util/world/BlockDsl.kt create mode 100644 common/src/main/kotlin/com/lambda/util/world/EntityDsl.kt create mode 100644 common/src/main/kotlin/com/lambda/util/world/FluidDsl.kt delete mode 100644 common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt diff --git a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt index 95989ced8..d624f9eb0 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt @@ -7,10 +7,7 @@ import com.lambda.interaction.rotation.Rotation.Companion.rotation import com.lambda.interaction.rotation.Rotation.Companion.rotationTo import com.lambda.threading.runSafe import com.lambda.util.math.VecUtils.distSq -import com.lambda.util.world.SearchContext -import com.lambda.util.world.WorldUtils.getFastEntities -import com.lambda.util.world.search -import com.lambda.util.world.toFastVec +import com.lambda.util.world.entitySearch import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.entity.LivingEntity import net.minecraft.entity.mob.MobEntity @@ -58,17 +55,15 @@ abstract class Targeting( } fun getTarget(): LivingEntity? = runSafe { - var best: LivingEntity? = null - val predicate = { entity: LivingEntity -> validate(player, entity) } - search { - best = minEntityBy(targetingRange, predicate, priority.factor) - } - - return@runSafe best + return@runSafe entitySearch { + range(targetingRange) + filter(predicate) + comparator { priority.factor(this, it) } + }.minBy() } } diff --git a/common/src/main/kotlin/com/lambda/core/annotations/InternalApi.kt b/common/src/main/kotlin/com/lambda/core/annotations/InternalApi.kt index 9cd70a634..6a023ee78 100644 --- a/common/src/main/kotlin/com/lambda/core/annotations/InternalApi.kt +++ b/common/src/main/kotlin/com/lambda/core/annotations/InternalApi.kt @@ -3,7 +3,6 @@ package com.lambda.core.annotations import kotlin.annotation.AnnotationTarget.* @RequiresOptIn( - level = RequiresOptIn.Level.WARNING, message = "Only use if you know what you are doing, no support will be provided whatsoever, use at your own risk", ) @Target(CLASS, FUNCTION, PROPERTY, ANNOTATION_CLASS, CONSTRUCTOR, PROPERTY_SETTER, PROPERTY_GETTER, TYPEALIAS) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index 83c2b6a99..57dac5710 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -4,11 +4,8 @@ import com.lambda.config.groups.InteractionSettings import com.lambda.config.groups.RotationSettings import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.concurrentListener -import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.util.world.search -import net.minecraft.entity.LivingEntity import net.minecraft.util.Hand object CrystalAura : Module( diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt index 9fa01c807..5e6b3b64d 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt @@ -5,12 +5,8 @@ import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.graphics.renderer.esp.builders.build import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.util.BlockUtils.blockPos -import com.lambda.util.math.VecUtils.blockPos -import com.lambda.util.world.search +import com.lambda.util.world.blockSearch import net.minecraft.block.Blocks -import net.minecraft.util.math.Box -import net.minecraft.util.math.Vec3d import net.minecraft.util.math.Vec3i import java.awt.Color @@ -37,16 +33,17 @@ object BlockTest : Module( init { listener { - search { - blocks( - range, - step - ) { _, block -> block.isOf(Blocks.DIAMOND_BLOCK) }.forEach { (pos, state) -> + blockSearch { + range(range) + step(step) + + filter { _, block -> block.isOf(Blocks.DIAMOND_BLOCK) } + iterator { pos, state -> state.getOutlineShape(world, pos).boundingBoxes.forEach { box -> it.renderer.build(box.offset(pos), filledColor, outlineColor) } } - } + }.build() } } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt index e691141a9..011a00540 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt @@ -7,7 +7,7 @@ import com.lambda.graphics.renderer.esp.builders.build import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.math.ColorUtils.setAlpha -import com.lambda.util.world.search +import com.lambda.util.world.entitySearch import net.minecraft.entity.LivingEntity import net.minecraft.util.math.Box import java.awt.Color @@ -29,10 +29,13 @@ object RenderTest : Module( init { listener { - search { - val entity = closestEntity(8.0) ?: return@search - it.renderer.build(entity.dynamicBox, filledColor, outlineColor) - } + entitySearch { + range(8) + + iterator { entity -> + it.renderer.build(entity.dynamicBox, filledColor, outlineColor) + } + }.build() } listener { diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/EntityControl.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/EntityControl.kt index 2b4958156..08948f05f 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/EntityControl.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/EntityControl.kt @@ -6,8 +6,7 @@ import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.util.world.WorldUtils.getEntities -import com.lambda.util.world.search +import com.lambda.util.world.entitySearch import net.minecraft.entity.passive.AbstractHorseEntity import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket @@ -26,8 +25,9 @@ object EntityControl : Module( init { listener { if (forceMount) { - search { - nearbyEntities(8.0) { it.setHorseFlag(4, true) } + entitySearch { + range(8) + iterator { it.setHorseFlag(4, true) } } } } @@ -49,8 +49,9 @@ object EntityControl : Module( } fun SafeContext.resetHorseFlags() { - search { - entities(500) { it.updateSaddle() } + entitySearch { + range(8) + iterator { it.updateSaddle() } } } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt index 17a63881f..769215c17 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt @@ -13,11 +13,9 @@ import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.util.Nameable import com.lambda.util.player.MovementUtils.addSpeed -import com.lambda.util.player.MovementUtils.buildMovementInput import com.lambda.util.player.MovementUtils.calcMoveYaw import com.lambda.util.player.MovementUtils.handledByBaritone import com.lambda.util.player.MovementUtils.isInputting -import com.lambda.util.player.MovementUtils.mergeFrom import com.lambda.util.player.MovementUtils.motionY import com.lambda.util.player.MovementUtils.moveDelta import com.lambda.util.player.MovementUtils.newMovementInput @@ -25,8 +23,7 @@ import com.lambda.util.player.MovementUtils.roundedForward import com.lambda.util.player.MovementUtils.roundedStrafing import com.lambda.util.player.MovementUtils.setSpeed import com.lambda.util.primitives.extension.contains -import com.lambda.util.world.WorldUtils.getFastEntities -import com.lambda.util.world.search +import com.lambda.util.world.entitySearch import net.minecraft.entity.LivingEntity import net.minecraft.entity.decoration.ArmorStandEntity import net.minecraft.entity.vehicle.BoatEntity @@ -135,20 +132,17 @@ object Speed : Module( var boostAmount = 0.0 - search { - nearbyEntities(3.0, - predicate = { player.boundingBox.expand(1.0) in it.boundingBox && it !is ArmorStandEntity } - ) { entity -> - val colliding = player.boundingBox in entity.boundingBox - val multiplier = if (colliding) grimCollideMultiplier else 1.0 - boostAmount += 0.08 * grimEntityBoost * multiplier - } - - if (grimBoatBoost > 0.0) { - nearbyEntities( - 4.0, - predicate = { player.boundingBox in it.boundingBox.expand(0.01) }, - ) { _ -> boostAmount += grimBoatBoost } + entitySearch { + range(3.0) + filter { player.boundingBox.expand(1.0) in it.boundingBox && it !is ArmorStandEntity } + iterator { boostAmount += 0.08 * grimEntityBoost } + } + + if (grimBoatBoost > 0.0) { + entitySearch { + range(4.0) + filter { player.boundingBox in it.boundingBox.expand(0.01) } + iterator { boostAmount += grimBoatBoost } } } diff --git a/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt b/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt index 6c4ab565f..2ab36c135 100644 --- a/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt +++ b/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt @@ -1,5 +1,7 @@ package com.lambda.util.collections +import kotlin.reflect.KClass + /** * Filters elements of the iterable by their runtime type and a predicate, and adds the matching elements to the specified mutable collection. * @@ -22,7 +24,7 @@ inline fun > Iterable<*>.filterPointer( var index = 0 for (element in this) { - val fulfilled = predicate(element as? R ?: continue) + val fulfilled = predicate(element as R ?: continue) if (fulfilled && destination != null) { destination.add(element) @@ -32,3 +34,33 @@ inline fun > Iterable<*>.filterPointer( index++ } } + +/** + * Filters elements of the iterable by their runtime type and a predicate, and adds the matching elements to the specified mutable collection. + * + * This function allows filtering elements of an iterable based on their runtime type and a provided predicate function. + * The elements that match both the type constraint and the predicate are added to the destination mutable collection. + * Because we do not want additional overhead, this function acts as pointer receiver to a collection. + * The predicate function determines whether an element should be included based on its type and any additional criteria. + * + * @param R The target type to filter elements to. + * @param C The type of the destination mutable collection. + * @param destination The mutable collection to which the filtered elements will be added. + * @param iterator The iterator function that processes the filtered elements and their index. + * @param predicate The predicate function that determines whether an element should be included based on its type and other criteria. + */ +inline fun > Iterable<*>.filterPointer( + kclass: KClass, + destination: C?, + iterator: (R) -> Unit, + predicate: (R) -> Boolean, +) { + for (element in this) { + val fulfilled = kclass.isInstance(element) && predicate(element as R) + + if (fulfilled && destination != null) { + destination.add(element as R) + iterator(element) + } + } +} diff --git a/common/src/main/kotlin/com/lambda/util/combat/Explosion.kt b/common/src/main/kotlin/com/lambda/util/combat/Explosion.kt index 854b76136..24fc433e0 100644 --- a/common/src/main/kotlin/com/lambda/util/combat/Explosion.kt +++ b/common/src/main/kotlin/com/lambda/util/combat/Explosion.kt @@ -8,7 +8,7 @@ import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.fluidState import com.lambda.util.math.VecUtils.minus import com.lambda.util.math.VecUtils.times -import com.lambda.util.world.WorldUtils.getFastEntities +import com.lambda.util.world.WorldUtils.internalGetFastEntities import com.lambda.util.world.toFastVec import net.minecraft.enchantment.ProtectionEnchantment import net.minecraft.entity.LivingEntity @@ -55,7 +55,7 @@ object Explosion { */ fun SafeContext.explosionVelocity(explosion: Explosion): Map { val ref = ArrayList() - getFastEntities(explosion.position.toFastVec(), explosion.power * 2.0, ref) + internalGetFastEntities(explosion.position.toFastVec(), explosion.power * 2.0, ref) return ref.associateWith { entity -> explosionVelocity(entity, explosion) } } diff --git a/common/src/main/kotlin/com/lambda/util/world/BlockDsl.kt b/common/src/main/kotlin/com/lambda/util/world/BlockDsl.kt new file mode 100644 index 000000000..84fc561a8 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/world/BlockDsl.kt @@ -0,0 +1,141 @@ +@file:OptIn(InternalApi::class) + +package com.lambda.util.world + +import com.lambda.context.SafeContext +import com.lambda.core.annotations.InternalApi +import com.lambda.util.world.WorldUtils.MAGICVECTOR +import com.lambda.util.world.WorldUtils.internalSearchBlocks +import net.minecraft.block.BlockState +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.Vec3i + +/** + * The `BlockDsl` class provides a DSL for performing block search operations + * within a specified range in a Minecraft world. It allows for filtering and iterating blocks + * around a given position. + * + * @param safeContext The context in which the block search is performed, providing safe access to world data. + * @param pos The position from which to start the block search. + * + * ### Usage Example: + * + * ```kotlin + * val blocks = blockSearch { + * range(10.0) // Search for blocks within a 10 block radius + * step(2.0) // Check for blocks every 2 block + * filter { _, block -> block.isOf(Blocks.DIAMOND_BLOCK) } // Filter out blocks that are not diamond blocks + * iterator { pos, state -> println("Found diamond block at: $pos") } // Print the position of each diamond block + * }.build() // Finalize the block search + * + * println("Found ${blocks.size} diamond blocks.") + * ``` + */ +class BlockDsl( + private val safeContext: SafeContext, + pos: BlockPos, +) { + private val fastVector = pos.toFastVec() + private val receiver: MutableMap = mutableMapOf() + + private var range: FastVector = MAGICVECTOR times 8 + private var step: FastVector = MAGICVECTOR + private var predicate: (FastVector, BlockState) -> Boolean = { _, _ -> true } + private var iterator: (FastVector, BlockState) -> Unit = { _, _ -> } + + /** + * Sets the vector representing the maximum distances from the position to search for blocks. + */ + fun range(range: Int): BlockDsl { + this.range = fastVectorOf(range, range, range) + return this + } + + /** + * Sets the vector representing the maximum distances from the position to search for blocks. + */ + fun range(range: Double): BlockDsl { + this.range = fastVectorOf(range.toInt(), range.toInt(), range.toInt()) + return this + } + + /** + * Sets the vector representing the maximum distances from the position to search for blocks. + */ + fun range(range: Vec3i): BlockDsl { + this.range = range.toFastVec() + return this + } + + /** + * Sets the vector representing the maximum distances from the position to search for blocks. + */ + fun range(range: Vec3d): BlockDsl { + this.range = range.toFastVec() + return this + } + + /** + * Sets the vector representing the intervals at which to check for blocks. + */ + fun step(step: Int): BlockDsl { + this.step = fastVectorOf(step, step, step) + return this + } + + /** + * Sets the vector representing the intervals at which to check for blocks. + */ + fun step(step: Double): BlockDsl { + this.step = fastVectorOf(step.toInt(), step.toInt(), step.toInt()) + return this + } + + /** + * Sets the vector representing the intervals at which to check for blocks. + */ + fun step(step: Vec3i): BlockDsl { + this.step = step.toFastVec() + return this + } + + /** + * Sets the vector representing the intervals at which to check for blocks. + */ + fun step(step: Vec3d): BlockDsl { + this.step = step.toFastVec() + return this + } + + /** + * Sets a predicate to filter blocks. + */ + fun filter(predicate: (BlockPos, BlockState) -> Boolean): BlockDsl { + this.predicate = { pos, state -> predicate(pos.toBlockPos(), state) } + return this + } + + /** + * Sets an iterator to perform operations on each block. + */ + fun iterator(iterator: (BlockPos, BlockState) -> Unit): BlockDsl { + this.iterator = { pos, state -> iterator(pos.toBlockPos(), state) } + return this + } + + fun build(): Map { + safeContext.internalSearchBlocks(fastVector, range, step, receiver, predicate, iterator) + return receiver.mapKeys { it.key.toBlockPos() } + } +} + +/** + * Searches for blocks around the player's position and applies the specified block operations. + * Don't forget to call [BlockDsl.build] to finalize the search. + * + * @param pos The position around which to search for blocks. + * @param block The block operations to apply. + */ +fun SafeContext.blockSearch(pos: BlockPos = player.blockPos, block: BlockDsl.() -> Unit) = BlockDsl(this, pos).apply(block) + diff --git a/common/src/main/kotlin/com/lambda/util/world/EntityDsl.kt b/common/src/main/kotlin/com/lambda/util/world/EntityDsl.kt new file mode 100644 index 000000000..c638a7ac9 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/world/EntityDsl.kt @@ -0,0 +1,143 @@ +@file:OptIn(InternalApi::class) + +package com.lambda.util.world + +import com.lambda.context.SafeContext +import com.lambda.core.annotations.InternalApi +import com.lambda.util.world.WorldUtils.internalGetEntities +import com.lambda.util.world.WorldUtils.internalGetFastEntities +import net.minecraft.entity.Entity +import net.minecraft.util.math.BlockPos +import kotlin.reflect.KClass + +/** + * The `EntityDsl` class provides a DSLfor performing entity search operations + * within a specified range in a Minecraft world. It allows for filtering, iterating, and comparing entities + * of a specific type [T] around a given position. + * + * @param safeContext The context in which the entity search is performed, providing safe access to world data. + * @param kClass The class of the entity type [T]. + * @param pos The position from which to start the entity search. + * + * ### Usage Example: + * + * ```kotlin + * val entities = entitySearch(player.blockPos) { + * range(10.0) // Search for entities within a 10 block radius + * filter { it.isAlive } // Filter out dead entities + * iterator { println("Found entity: ${it.name.string}") } // Print the name of each entity + * }.build() // Finalize the entity search, you can also use `buildFast` for optimized performances. + * + * println("Found ${entities.size} entities.") + * ``` + * + * ```kotlin + * val closestEntityToFOV = entitySearch { + * range(10.0) // Search for entities within a 10 block radius + * filter { it.isAlive } // Filter out dead entities + * comparator { player.rotation dist player.eyePos.rotationTo(it.pos) } // Compare entities based on their distance to the player's field of view + * }.minBy() // Finalize the entity search and return the entity with the smallest value based on the comparator + * + * println("Closest entity to FOV: ${closestEntityToFOV}") + * ``` + */ +class EntityDsl( + private val safeContext: SafeContext, + private val kClass: KClass, + pos: BlockPos, +) { + private val fastVector = pos.toFastVec() + private val receiver: MutableList = mutableListOf() + + private var range: Double = 8.0 + private var predicate: (T) -> Boolean = { true } + private var iterator: (T) -> Unit = { _ -> } + private var comparator: SafeContext.(T) -> Double = { 0.0 } + + /** + * Sets the range around the position to search for entities. + */ + fun range(range: Int): EntityDsl { + this.range = range.toDouble() + return this + } + + /** + * Sets the range around the position to search for entities. + */ + fun range(range: Double): EntityDsl { + this.range = range + return this + } + + /** + * Sets a predicate to filter entities. + */ + fun filter(predicate: (T) -> Boolean): EntityDsl { + this.predicate = predicate + return this + } + + /** + * Sets an iterator to perform operations on each entity. + */ + fun iterator(iterator: (T) -> Unit): EntityDsl { + this.iterator = iterator + return this + } + + /** + * Sets a comparator to compare entities. + */ + fun comparator(comparator: SafeContext.(T) -> Double): EntityDsl { + this.comparator = comparator + return this + } + + /** + * Returns the entity that has the smallest value based on the given comparator. + * If multiple entities have the same value, the first one found will be returned. + * @return The entity with the smallest value based on the comparator, or null if no entity is found. + */ + fun minBy(): T? { + var min: T? = null + var minValue = Double.MAX_VALUE + + buildFast().forEach { entity -> + val value = safeContext.comparator(entity) + if (value < minValue) { + min = entity + minValue = value + } + } + + return min + } + + /** + * Retrieves a list of entities of type [T] within the specified range. + * This method is safe to use with mutable lists and should be used when the optimized method is not suitable. + */ + fun build(): List { + safeContext.internalGetEntities(kClass, fastVector, range, receiver, predicate, iterator) + return receiver + } + + /** + * Retrieves a list of entities of type [T] within the specified range. + * This method is optimized for performance and should be used when possible. + */ + fun buildFast(): List { + safeContext.internalGetFastEntities(kClass, fastVector, range, receiver, predicate, iterator) + return receiver + } +} + +/** + * Initiates an entity search operation in the world at the specified position using an [EntityDsl]. + * Don't forget to call [EntityDsl.build] or [EntityDsl.buildFast] to finalize the search. + * + * @param pos The position to start the search from. Defaults to the player's current position. + * @param block The block of code that performs the search using the [EntityDsl]. + */ +inline fun SafeContext.entitySearch(pos: BlockPos = player.blockPos, block: EntityDsl.() -> Unit) = EntityDsl(this, T::class, pos).apply(block) diff --git a/common/src/main/kotlin/com/lambda/util/world/FluidDsl.kt b/common/src/main/kotlin/com/lambda/util/world/FluidDsl.kt new file mode 100644 index 000000000..57cc62275 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/world/FluidDsl.kt @@ -0,0 +1,146 @@ +@file:OptIn(InternalApi::class) + +package com.lambda.util.world + +import com.lambda.context.SafeContext +import com.lambda.core.annotations.InternalApi +import com.lambda.util.world.WorldUtils.MAGICVECTOR +import com.lambda.util.world.WorldUtils.internalSearchFluids +import net.minecraft.fluid.Fluid +import net.minecraft.fluid.FluidState +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.Vec3i +import kotlin.reflect.KClass + +/** + * The `FluidDsl` class provides a DSL for performing fluid search operations + * within a specified range in a Minecraft world. It allows for filtering and iterating fluids + * of a specific type [T] around a given position. + * + * @param safeContext The context in which the fluid search is performed, providing safe access to world data. + * @param kClass The class of the fluid type [T]. + * @param pos The position from which to start the fluid search. + * + * ### Usage Example: + * + * ```kotlin + * val fluids = fluidSearch { + * range(8) // Search for fluids within an 8 block radius + * iterator { pos, state -> println("Found immobile lava at $pos with state $state") } + * }.build() // Finalize the fluid search + * + * println("Found ${fluids.size} immobile lava fluids.") + * ``` + */ +class FluidDsl( + private val safeContext: SafeContext, + private val kClass: KClass, + pos: BlockPos, +) { + private val fastVector = pos.toFastVec() + private val receiver: MutableMap = mutableMapOf() + + private var range: FastVector = MAGICVECTOR times 8 + private var step: FastVector = MAGICVECTOR + private var predicate: (FastVector, FluidState) -> Boolean = { _, _ -> true } + private var iterator: (FastVector, FluidState) -> Unit = { _, _ -> } + + /** + * Sets the range around the position to search for fluids. + */ + fun range(range: Double): FluidDsl { + this.range = fastVectorOf(range.toInt(), range.toInt(), range.toInt()) + return this + } + + /** + * Sets the range around the position to search for fluids. + */ + fun range(range: Int): FluidDsl { + this.range = fastVectorOf(range, range, range) + return this + } + + /** + * Sets the range around the position to search for fluids. + */ + fun range(range: Vec3i): FluidDsl { + this.range = range.toFastVec() + return this + } + + /** + * Sets the range around the position to search for fluids. + */ + fun range(range: Vec3d): FluidDsl { + this.range = range.toFastVec() + return this + } + + /** + * Sets the vector representing the intervals at which to check for blocks. + */ + fun step(step: Int): FluidDsl { + this.step = fastVectorOf(step, step, step) + return this + } + + /** + * Sets the vector representing the intervals at which to check for blocks. + */ + fun step(step: Double): FluidDsl { + this.step = fastVectorOf(step.toInt(), step.toInt(), step.toInt()) + return this + } + + /** + * Sets the vector representing the intervals at which to check for blocks. + */ + fun step(step: Vec3i): FluidDsl { + this.step = step.toFastVec() + return this + } + + /** + * Sets the vector representing the intervals at which to check for blocks. + */ + fun step(step: Vec3d): FluidDsl { + this.step = step.toFastVec() + return this + } + + /** + * Sets a predicate to filter fluids. + */ + fun filter(predicate: (BlockPos, FluidState) -> Boolean): FluidDsl { + this.predicate = { pos, state -> predicate(pos.toBlockPos(), state) } + return this + } + + /** + * Sets an iterator to perform operations on each fluid. + */ + fun iterator(iterator: (BlockPos, FluidState) -> Unit): FluidDsl { + this.iterator = { pos, state -> iterator(pos.toBlockPos(), state) } + return this + } + + /** + * Builds the map of fluids found in the world. + */ + fun build(): Map { + safeContext.internalSearchFluids(kClass, fastVector, range, step, receiver, predicate, iterator) + + return receiver.mapKeys { it.key.toBlockPos() } + } +} + +/** + * Initiates a fluid search operation in the world at the specified position using a [FluidDsl]. + * The fluid search operation is performed using the specified block of code. + * + * @param pos The position to start the search from. Defaults to the player's current position. + * @param block The block of code that performs the search using the [FluidDsl]. + */ +inline fun SafeContext.fluidSearch(pos: BlockPos = player.blockPos, block: FluidDsl.() -> Unit) = FluidDsl(this, T::class, pos).apply(block) diff --git a/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt b/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt deleted file mode 100644 index feebad7d0..000000000 --- a/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt +++ /dev/null @@ -1,218 +0,0 @@ -@file:OptIn(InternalApi::class) - -package com.lambda.util.world - -import com.lambda.Lambda.LOG -import com.lambda.context.SafeContext -import com.lambda.core.annotations.InternalApi -import com.lambda.util.math.VecUtils.distSq -import com.lambda.util.world.WorldUtils.getEntities -import com.lambda.util.world.WorldUtils.getFastEntities -import com.lambda.util.world.WorldUtils.searchBlocks -import com.lambda.util.world.WorldUtils.searchFluids -import net.minecraft.block.BlockState -import net.minecraft.entity.Entity -import net.minecraft.fluid.Fluid -import net.minecraft.fluid.FluidState -import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Vec3i - -@DslMarker -annotation class WorldDsl - -/** - * A context for performing various high-level search operations within the world. - * These functions prioritize ease of use and flexibility, potentially at the cost - * of performance. - * - * @property safeContext The context that ensures safe operations within the world. - * @property pos The position from which searches are conducted. - */ -@WorldDsl -class SearchContext(val safeContext: SafeContext, val pos: BlockPos) { - val fastVector: FastVector = pos.toFastVec() - - /** - * Finds the closest entity of type [T] to the specified position within a given range. - * - * @param range The maximum distance from the position to search for entities. - * @param predicate A filter to determine which entities to include in the search. - * @return The closest entity of type [T] that matches the predicate, or null if no entity is found. - */ - inline fun closestEntity( - range: Double, - predicate: (T) -> Boolean = { true }, - ): T? = minEntityBy(range, predicate) { entity -> pos distSq entity.pos } - - /** - * Returns the entity that has the smallest value based on the given comparator. - * If multiple entities have the same value, the first one found will be returned. - * - * @param range The maximum distance from the position to search for entities. - * @param comparator A function that compares two entities and returns a value. - * @param predicate A filter to determine which entities to include in the search. - * @return The entity with the smallest value based on the comparator, or null if no entity is found. - */ - inline fun minEntityBy( - range: Double, - predicate: (T) -> Boolean = { true }, - comparator: SafeContext.(T) -> Double, - ): T? { - var min: T? = null - var minValue = Double.MAX_VALUE - - safeContext.getFastEntities(fastVector, range, predicate = predicate) { entity, _ -> - val value = comparator(safeContext, entity) - - if (value < minValue) { - min = entity - minValue = value - } - } - - return min - } - - /** - * Retrieves a list of nearby entities of type [T] within the specified range. - * - * @param range The radius around the position to search for entities. - * @param predicate A filter to determine which entities to include in the search. - * @return A list of entities of type [T] - */ - inline fun nearbyEntities( - range: Double, - predicate: (T) -> Boolean = { true }, - iterator: (T) -> Unit = { _ -> }, - ): List { - if (range >= 64) { - LOG.warn("Searching nearby entities with a range of $range may be slow. " + - "Consider reducing the range to improve performance or using a linear search instead.") - } - - val entities = mutableListOf() - - safeContext.getFastEntities(fastVector, range, entities, predicate) { entity, _ -> iterator(entity) } - - return entities - } - - /** - * Retrieves a list of entities of type [T] within the specified range. - * - * @param range The radius around the position to search for entities. - * @param predicate A filter to determine which entities to include in the search. - * @return A list of entities of type [T]. - */ - inline fun entities( - range: Int, - predicate: (T) -> Boolean = { true }, - iterator: (T) -> Unit = { _ -> }, - ): List { - val entities = mutableListOf() - - safeContext.getEntities(fastVector, range.toDouble(), entities, predicate) { entity, _ -> iterator(entity) } - - return entities - } - - /** - * Searches for blocks within the specified range and step size. - * - * @param range The maximum distance from the position to search for blocks. - * @param step The interval at which to check for blocks. Defaults to 1. - * @param predicate A filter to determine which blocks to include in the search. Defaults to always true. - * @return A map of positions to block states. - */ - inline fun blocks( - range: Int, - step: Int = 1, - crossinline predicate: (BlockPos, BlockState) -> Boolean = { _, _ -> true }, - crossinline iterator: (BlockPos, BlockState) -> Unit = { _, _ -> }, - ): Map = blocks( - BlockPos(range, range, range), - BlockPos(step, step, step), - predicate, iterator, - ) - - /** - * Searches for blocks within the specified 3D range and step size, with an optional iterator function. - * - * @param range The 3D vector representing the maximum distances from the position to search for blocks. - * @param step The 3D vector representing the intervals at which to check for blocks. - * @param predicate A filter to determine which blocks to include in the search. - * @param iterator A function to be called for each block found, allowing additional processing - * @return A map of positions to block states. - */ - inline fun blocks( - range: Vec3i, - step: Vec3i = Vec3i(1, 1, 1), - crossinline predicate: (BlockPos, BlockState) -> Boolean = { _, _ -> true }, - crossinline iterator: (BlockPos, BlockState) -> Unit = { _, _ -> }, - ): Map { - val blocks = mutableMapOf() - - val transformedPredicate: (FastVector, BlockState, Int) -> Boolean = - { fast, state, _ -> predicate(fast.toBlockPos(), state) } - val transformedIterator: (FastVector, BlockState, Int) -> Unit = - { fast, state, _ -> iterator(fast.toBlockPos(), state) } - - safeContext.searchBlocks(fastVector, range.toFastVec(), step.toFastVec(), blocks, transformedPredicate, transformedIterator) - - return blocks.mapKeys { it.key.toBlockPos() } - } - - /** - * Searches for fluids within the specified range and step size. - * - * @param range The maximum distance from the position to search for fluids. - * @param step The interval at which to check for fluids. - * @param predicate A filter to determine which fluids to include in the search. - * @return A map of positions to fluids. - */ - inline fun fluids( - range: Int, - step: Int = 1, - crossinline predicate: (BlockPos, FluidState) -> Boolean = { _, _ -> true }, - crossinline iterator: (BlockPos, FluidState) -> Unit = { _, _ -> }, - ): Map = fluids( - BlockPos(range, range, range), - BlockPos(step, step, step), - predicate, iterator, - ) - - /** - * Searches for fluids within the specified 3D range and step size, with an optional iterator function. - * - * @param range The vector representing the maximum distances from the position to search for fluids. - * @param step The vector representing the intervals at which to check for fluids. - * @param predicate A filter to determine which fluids to include in the search. - * @param iterator A function to be called for each fluid found, allowing additional processing. - * @return A map of positions to fluids. - */ - inline fun fluids( - range: Vec3i, - step: Vec3i = Vec3i(1, 1, 1), - crossinline predicate: (BlockPos, FluidState) -> Boolean = { _, _ -> true }, - crossinline iterator: (BlockPos, FluidState) -> Unit = { _, _ -> }, - ): Map { - val fluids = mutableMapOf() - - val transformedPredicate: (FastVector, FluidState, Int) -> Boolean = - { fast, state, _ -> predicate(fast.toBlockPos(), state) } - val transformedIterator: (FastVector, FluidState, Int) -> Unit = - { fast, state, _ -> iterator(fast.toBlockPos(), state) } - - safeContext.searchFluids(pos.toFastVec(), range.toFastVec(), step.toFastVec(), fluids, transformedPredicate, transformedIterator) - return fluids.mapKeys { it.key.toBlockPos() } - } -} - -/** - * Initiates a search operation in the world at the specified position using a [SearchContext]. - * - * @param pos The position to start the search from. Defaults to the player's current position. - * @param block The block of code that performs the search using the [SearchContext]. - */ -inline fun SafeContext.search(pos: BlockPos = player.blockPos, block: SearchContext.() -> Unit) = - SearchContext(this, pos).apply(block) diff --git a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt index ae45313b4..b4460b0ac 100644 --- a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt @@ -11,6 +11,7 @@ import net.minecraft.fluid.Fluid import net.minecraft.fluid.FluidState import net.minecraft.util.math.ChunkSectionPos import kotlin.math.ceil +import kotlin.reflect.KClass /** * Utility functions for working with the Minecraft world. @@ -39,12 +40,12 @@ import kotlin.math.ceil * @see IBM - Garbage Collection Impacts on Java Performance * @see Medium - GC and Its Effect on Java Performance */ -@InternalApi object WorldUtils { /** * A magic vector that can be used to represent a single block. * It is the same as `fastVectorOf(1, 1, 1)`. */ + @InternalApi const val MAGICVECTOR = 274945015809L /** @@ -61,7 +62,7 @@ object WorldUtils { * ``` * * Please note that this implementation is optimized for performance at small distances. - * For larger distances, it is recommended to use the [getEntities] function instead. + * For larger distances, it is recommended to use the [internalGetEntities] function instead. * With the time complexity, we can determine that the performance of this function will degrade after 64 blocks. * * @param pos The position to search from. @@ -70,12 +71,23 @@ object WorldUtils { * @param iterator Iterator to perform operations on each entity. The second parameter is the index of the iteration. * @param predicate Predicate to filter entities. */ - inline fun SafeContext.getFastEntities( + @InternalApi + inline fun SafeContext.internalGetFastEntities( pos: FastVector, distance: Double, pointer: MutableList? = null, predicate: (T) -> Boolean = { true }, - iterator: (T, Int) -> Unit = { _, _ -> }, + iterator: (T) -> Unit = { _ -> }, + ) = internalGetFastEntities(T::class, pos, distance, pointer, predicate, iterator) + + @InternalApi + inline fun SafeContext.internalGetFastEntities( + kClass: KClass, + pos: FastVector, + distance: Double, + pointer: MutableList? = null, + predicate: (T) -> Boolean = { true }, + iterator: (T) -> Unit = { _ -> }, ) { val chunks = ceil(distance / 16.0).toInt() val sectionX = pos.x shr 4 @@ -93,7 +105,7 @@ object WorldUtils { .cache .findTrackingSection(ChunkSectionPos.asLong(x, y, z)) ?: continue - section.collection.filterPointer(pointer, iterator) { entity -> + section.collection.filterPointer(kClass, pointer, iterator) { entity -> entity != player && pos distSq entity.pos <= distance * distance && predicate(entity) @@ -107,7 +119,7 @@ object WorldUtils { * Gets all entities of type [T] within a specified distance from a position. * * This function retrieves entities of type [T] within a specified distance from a given position. Unlike - * [getFastEntities], it traverses all entities in the world to find matches, while also excluding the player entity. + * [internalGetFastEntities], it traverses all entities in the world to find matches, while also excluding the player entity. * * @param pos The block position to search from. * @param distance The maximum distance to search for entities. @@ -115,14 +127,25 @@ object WorldUtils { * @param iterator Iterator to perform operations on each entity. The second parameter is the index of the iteration. * @param predicate Predicate to filter entities. */ - inline fun SafeContext.getEntities( + @InternalApi + inline fun SafeContext.internalGetEntities( pos: FastVector, distance: Double, pointer: MutableList? = null, predicate: (T) -> Boolean = { true }, - iterator: (T, Int) -> Unit = { _, _ -> }, + iterator: (T) -> Unit = { _ -> }, + ) = internalGetEntities(T::class, pos, distance, pointer, predicate, iterator) + + @InternalApi + inline fun SafeContext.internalGetEntities( + kClass: KClass, + pos: FastVector, + distance: Double, + pointer: MutableList? = null, + predicate: (T) -> Boolean = { true }, + iterator: (T) -> Unit = { _ -> }, ) { - world.entities.filterPointer(pointer, iterator) { entity -> + world.entities.filterPointer(kClass, pointer, iterator) { entity -> entity != player && pos distSq entity.pos <= distance * distance && predicate(entity) @@ -138,21 +161,22 @@ object WorldUtils { * @param iterator Iterator to perform operations on each block. * @param predicate Predicate to filter the blocks. */ - inline fun SafeContext.searchBlocks( + @InternalApi + inline fun SafeContext.internalSearchBlocks( pos: FastVector, range: FastVector = MAGICVECTOR times 7, step: FastVector = MAGICVECTOR, pointer: MutableMap? = null, - predicate: (FastVector, BlockState, Int) -> Boolean = { _, _, _ -> true }, - iterator: (FastVector, BlockState, Int) -> Unit = { _, _, _ -> }, + predicate: (FastVector, BlockState) -> Boolean = { _, _ -> true }, + iterator: (FastVector, BlockState) -> Unit = { _, _ -> }, ) { - iteratePositions(pos, range, step) { position, index -> + internalIteratePositions(pos, range, step) { position -> world.getBlockState(position.x, position.y, position.z).let { state -> - val fulfilled = predicate(position, state, index) + val fulfilled = predicate(position, state) if (fulfilled && pointer != null) { pointer[position] = state - iterator(position, state, index) + iterator(position, state) } } } @@ -167,21 +191,34 @@ object WorldUtils { * @param iterator Iterator to perform operations on each fluid. * @param predicate Predicate to filter the fluids. */ - inline fun SafeContext.searchFluids( + @InternalApi + inline fun SafeContext.internalSearchFluids( + pos: FastVector, + range: FastVector = MAGICVECTOR times 7, + step: FastVector = MAGICVECTOR, + pointer: MutableMap? = null, + predicate: (FastVector, FluidState) -> Boolean = { _, _ -> true }, + iterator: (FastVector, FluidState) -> Unit = { _, _ -> }, + ) = internalSearchFluids(T::class, pos, range, step, pointer, predicate, iterator) + + @InternalApi + inline fun SafeContext.internalSearchFluids( + kClass: KClass, pos: FastVector, range: FastVector = MAGICVECTOR times 7, step: FastVector = MAGICVECTOR, pointer: MutableMap? = null, - predicate: (FastVector, FluidState, Int) -> Boolean = { _, _, _ -> true }, - iterator: (FastVector, FluidState, Int) -> Unit = { _, _, _ -> }, + predicate: (FastVector, FluidState) -> Boolean = { _, _ -> true }, + iterator: (FastVector, FluidState) -> Unit = { _, _ -> }, ) { - iteratePositions(pos, range, step) { position, index -> + internalIteratePositions(pos, range, step) { position -> world.getFluidState(position.x, position.y, position.z).let { state -> - val fulfilled = predicate(position, state, index) + val fulfilled = kClass.isInstance(state.fluid) && predicate(position, state) if (fulfilled && pointer != null) { pointer[position] = state.fluid as T - iterator(position, state, index) + + iterator(position, state) } } } @@ -194,20 +231,18 @@ object WorldUtils { * @param step The step to increment the position by. * @param iterator Iterator to perform operations on each position. */ - inline fun iteratePositions( + @InternalApi + inline fun internalIteratePositions( pos: FastVector, range: FastVector, step: FastVector, - iterator: (FastVector, Int) -> Unit = { _, _ -> }, + iterator: (FastVector) -> Unit = { _ -> }, ) { - var index = 0 - for (x in -range.x..range.x step step.x) { for (y in -range.y..range.y step step.y) { for (z in -range.z..range.z step step.z) { iterator( pos plus fastVectorOf(x, y, z), - index++ ) } } From 3247c70e7427f4209d94c6fcc8dbb2cff56a1c97 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sat, 24 Aug 2024 14:12:29 -0400 Subject: [PATCH 09/11] wrong import location --- .../src/main/kotlin/com/lambda/module/modules/movement/Speed.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt index 769215c17..4fbcadb24 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt @@ -22,7 +22,7 @@ import com.lambda.util.player.MovementUtils.newMovementInput import com.lambda.util.player.MovementUtils.roundedForward import com.lambda.util.player.MovementUtils.roundedStrafing import com.lambda.util.player.MovementUtils.setSpeed -import com.lambda.util.primitives.extension.contains +import com.lambda.util.extension.contains import com.lambda.util.world.entitySearch import net.minecraft.entity.LivingEntity import net.minecraft.entity.decoration.ArmorStandEntity From 0c11318e3e336c2afae92f80e860a91acd616d7a Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sat, 24 Aug 2024 14:34:32 -0400 Subject: [PATCH 10/11] added dsl markers --- common/src/main/kotlin/com/lambda/util/world/BlockDsl.kt | 7 ++++++- common/src/main/kotlin/com/lambda/util/world/EntityDsl.kt | 7 ++++++- common/src/main/kotlin/com/lambda/util/world/FluidDsl.kt | 7 ++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/util/world/BlockDsl.kt b/common/src/main/kotlin/com/lambda/util/world/BlockDsl.kt index 84fc561a8..fefba0154 100644 --- a/common/src/main/kotlin/com/lambda/util/world/BlockDsl.kt +++ b/common/src/main/kotlin/com/lambda/util/world/BlockDsl.kt @@ -11,6 +11,10 @@ import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3d import net.minecraft.util.math.Vec3i +@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE) +@DslMarker +annotation class BlockDslMarker + /** * The `BlockDsl` class provides a DSL for performing block search operations * within a specified range in a Minecraft world. It allows for filtering and iterating blocks @@ -32,6 +36,7 @@ import net.minecraft.util.math.Vec3i * println("Found ${blocks.size} diamond blocks.") * ``` */ +@BlockDslMarker class BlockDsl( private val safeContext: SafeContext, pos: BlockPos, @@ -137,5 +142,5 @@ class BlockDsl( * @param pos The position around which to search for blocks. * @param block The block operations to apply. */ -fun SafeContext.blockSearch(pos: BlockPos = player.blockPos, block: BlockDsl.() -> Unit) = BlockDsl(this, pos).apply(block) +fun SafeContext.blockSearch(pos: BlockPos = player.blockPos, block: (@BlockDslMarker BlockDsl).() -> Unit) = BlockDsl(this, pos).apply(block) diff --git a/common/src/main/kotlin/com/lambda/util/world/EntityDsl.kt b/common/src/main/kotlin/com/lambda/util/world/EntityDsl.kt index c638a7ac9..fc53744a3 100644 --- a/common/src/main/kotlin/com/lambda/util/world/EntityDsl.kt +++ b/common/src/main/kotlin/com/lambda/util/world/EntityDsl.kt @@ -10,6 +10,10 @@ import net.minecraft.entity.Entity import net.minecraft.util.math.BlockPos import kotlin.reflect.KClass +@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE) +@DslMarker +annotation class EntityDslMarker + /** * The `EntityDsl` class provides a DSLfor performing entity search operations * within a specified range in a Minecraft world. It allows for filtering, iterating, and comparing entities @@ -41,6 +45,7 @@ import kotlin.reflect.KClass * println("Closest entity to FOV: ${closestEntityToFOV}") * ``` */ +@EntityDslMarker class EntityDsl( private val safeContext: SafeContext, private val kClass: KClass, @@ -140,4 +145,4 @@ class EntityDsl( * @param pos The position to start the search from. Defaults to the player's current position. * @param block The block of code that performs the search using the [EntityDsl]. */ -inline fun SafeContext.entitySearch(pos: BlockPos = player.blockPos, block: EntityDsl.() -> Unit) = EntityDsl(this, T::class, pos).apply(block) +inline fun SafeContext.entitySearch(pos: BlockPos = player.blockPos, block: (@EntityDslMarker EntityDsl).() -> Unit) = EntityDsl(this, T::class, pos).apply(block) diff --git a/common/src/main/kotlin/com/lambda/util/world/FluidDsl.kt b/common/src/main/kotlin/com/lambda/util/world/FluidDsl.kt index 57cc62275..32a8a3e2f 100644 --- a/common/src/main/kotlin/com/lambda/util/world/FluidDsl.kt +++ b/common/src/main/kotlin/com/lambda/util/world/FluidDsl.kt @@ -13,6 +13,10 @@ import net.minecraft.util.math.Vec3d import net.minecraft.util.math.Vec3i import kotlin.reflect.KClass +@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE) +@DslMarker +annotation class FluidDslMarker + /** * The `FluidDsl` class provides a DSL for performing fluid search operations * within a specified range in a Minecraft world. It allows for filtering and iterating fluids @@ -33,6 +37,7 @@ import kotlin.reflect.KClass * println("Found ${fluids.size} immobile lava fluids.") * ``` */ +@FluidDslMarker class FluidDsl( private val safeContext: SafeContext, private val kClass: KClass, @@ -143,4 +148,4 @@ class FluidDsl( * @param pos The position to start the search from. Defaults to the player's current position. * @param block The block of code that performs the search using the [FluidDsl]. */ -inline fun SafeContext.fluidSearch(pos: BlockPos = player.blockPos, block: FluidDsl.() -> Unit) = FluidDsl(this, T::class, pos).apply(block) +inline fun SafeContext.fluidSearch(pos: BlockPos = player.blockPos, block: (@FluidDslMarker FluidDsl).() -> Unit) = FluidDsl(this, T::class, pos).apply(block) From bab19fd974d399d252d4e773e47b881147493da5 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sun, 25 Aug 2024 06:40:29 +0200 Subject: [PATCH 11/11] Better DSL --- .../com/lambda/config/groups/Targeting.kt | 10 +- .../lambda/module/modules/debug/BlockTest.kt | 16 +- .../lambda/module/modules/debug/RenderTest.kt | 7 +- .../module/modules/movement/EntityControl.kt | 24 ++- .../lambda/module/modules/movement/Speed.kt | 16 +- .../com/lambda/util/collections/Extensions.kt | 14 +- .../lambda/util/primitives/extension/World.kt | 7 + .../kotlin/com/lambda/util/world/BlockDsl.kt | 141 +++++------------ .../kotlin/com/lambda/util/world/EntityDsl.kt | 129 +++++---------- .../kotlin/com/lambda/util/world/FluidDsl.kt | 147 ++++++------------ .../com/lambda/util/world/WorldUtils.kt | 6 +- 11 files changed, 173 insertions(+), 344 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt index d624f9eb0..11f248bd7 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/Targeting.kt @@ -59,11 +59,11 @@ abstract class Targeting( validate(player, entity) } - return@runSafe entitySearch { - range(targetingRange) - filter(predicate) - comparator { priority.factor(this, it) } - }.minBy() + return@runSafe entitySearch(targetingRange) { + predicate(it) + }.minBy { + priority.factor(this, it) + } } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt index 5e6b3b64d..7eeee03ff 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/BlockTest.kt @@ -33,17 +33,13 @@ object BlockTest : Module( init { listener { - blockSearch { - range(range) - step(step) - - filter { _, block -> block.isOf(Blocks.DIAMOND_BLOCK) } - iterator { pos, state -> - state.getOutlineShape(world, pos).boundingBoxes.forEach { box -> - it.renderer.build(box.offset(pos), filledColor, outlineColor) - } + blockSearch(range, step) { _, state -> + state.isOf(Blocks.DIAMOND_BLOCK) + }.forEach { (pos, state) -> + state.getOutlineShape(world, pos).boundingBoxes.forEach { box -> + it.renderer.build(box.offset(pos), filledColor, outlineColor) } - }.build() + } } } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt index 011a00540..c1b6be5ec 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/RenderTest.kt @@ -29,13 +29,10 @@ object RenderTest : Module( init { listener { - entitySearch { - range(8) - - iterator { entity -> + entitySearch(8.0) + .forEach { entity -> it.renderer.build(entity.dynamicBox, filledColor, outlineColor) } - }.build() } listener { diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/EntityControl.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/EntityControl.kt index 08948f05f..29f45947f 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/EntityControl.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/EntityControl.kt @@ -1,6 +1,5 @@ package com.lambda.module.modules.movement -import com.lambda.context.SafeContext import com.lambda.event.events.PacketEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener @@ -18,18 +17,20 @@ object EntityControl : Module( ) { private val forceMount by setting("Force Mount", true, description = "Attempts to force mount chested entities.").apply { onValueChange { _, _ -> - resetHorseFlags() + resetMounts() } } + private val modified = mutableSetOf() init { listener { - if (forceMount) { - entitySearch { - range(8) - iterator { it.setHorseFlag(4, true) } + if (!forceMount) return@listener + + entitySearch(8.0) + .forEach { + it.setHorseFlag(4, true) + modified.add(it) } - } } listener { event -> @@ -44,14 +45,11 @@ object EntityControl : Module( } onDisable { - resetHorseFlags() + resetMounts() } } - fun SafeContext.resetHorseFlags() { - entitySearch { - range(8) - iterator { it.updateSaddle() } - } + private fun resetMounts() { + modified.forEach { it.updateSaddle() } } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt index 4fbcadb24..0ba412a2f 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt @@ -132,18 +132,14 @@ object Speed : Module( var boostAmount = 0.0 - entitySearch { - range(3.0) - filter { player.boundingBox.expand(1.0) in it.boundingBox && it !is ArmorStandEntity } - iterator { boostAmount += 0.08 * grimEntityBoost } - } + boostAmount += entitySearch(3.0) { + player.boundingBox.expand(1.0) in it.boundingBox && it !is ArmorStandEntity + }.sumOf { 0.08 * grimEntityBoost } if (grimBoatBoost > 0.0) { - entitySearch { - range(4.0) - filter { player.boundingBox in it.boundingBox.expand(0.01) } - iterator { boostAmount += grimBoatBoost } - } + boostAmount += entitySearch(4.0) { + player.boundingBox in it.boundingBox.expand(0.01) + }.sumOf { grimBoatBoost } } addSpeed(boostAmount) diff --git a/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt b/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt index 2ab36c135..01fae338e 100644 --- a/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt +++ b/common/src/main/kotlin/com/lambda/util/collections/Extensions.kt @@ -23,8 +23,8 @@ inline fun > Iterable<*>.filterPointer( ) { var index = 0 - for (element in this) { - val fulfilled = predicate(element as R ?: continue) + forEach { element -> + val fulfilled = predicate(element as R) if (fulfilled && destination != null) { destination.add(element) @@ -50,16 +50,18 @@ inline fun > Iterable<*>.filterPointer( * @param predicate The predicate function that determines whether an element should be included based on its type and other criteria. */ inline fun > Iterable<*>.filterPointer( - kclass: KClass, + kClass: KClass, destination: C?, iterator: (R) -> Unit, predicate: (R) -> Boolean, ) { - for (element in this) { - val fulfilled = kclass.isInstance(element) && predicate(element as R) + forEach { element -> + // Cannot be replaced with reified type due to type erasure + (element as? R) ?: return@forEach + val fulfilled = kClass.isInstance(element) && predicate(element) if (fulfilled && destination != null) { - destination.add(element as R) + destination.add(element) iterator(element) } } diff --git a/common/src/main/kotlin/com/lambda/util/primitives/extension/World.kt b/common/src/main/kotlin/com/lambda/util/primitives/extension/World.kt index db5dfd1f1..fa4a053b5 100644 --- a/common/src/main/kotlin/com/lambda/util/primitives/extension/World.kt +++ b/common/src/main/kotlin/com/lambda/util/primitives/extension/World.kt @@ -1,5 +1,9 @@ package com.lambda.util.primitives.extension +import com.lambda.util.world.FastVector +import com.lambda.util.world.x +import com.lambda.util.world.y +import com.lambda.util.world.z import net.minecraft.block.BlockState import net.minecraft.block.Blocks import net.minecraft.fluid.FluidState @@ -27,3 +31,6 @@ fun World.getFluidState(x: Int, y: Int, z: Int): FluidState { val section = chunk.getSection(sectionIndex) return section.getFluidState(x and 0xF, y and 0xF, z and 0xF) } + +fun World.getBlockState(vec: FastVector): BlockState = getBlockState(vec.x, vec.y, vec.z) +fun World.getFluidState(vec: FastVector): FluidState = getFluidState(vec.x, vec.y, vec.z) diff --git a/common/src/main/kotlin/com/lambda/util/world/BlockDsl.kt b/common/src/main/kotlin/com/lambda/util/world/BlockDsl.kt index fefba0154..c14fb2fae 100644 --- a/common/src/main/kotlin/com/lambda/util/world/BlockDsl.kt +++ b/common/src/main/kotlin/com/lambda/util/world/BlockDsl.kt @@ -16,9 +16,9 @@ import net.minecraft.util.math.Vec3i annotation class BlockDslMarker /** - * The `BlockDsl` class provides a DSL for performing block search operations - * within a specified range in a Minecraft world. It allows for filtering and iterating blocks - * around a given position. + * The [BlockDsl] class provides a DSL for performing block search operations + * within a specified range in a Minecraft world. + * It allows for filtering blocks around a given position. * * @param safeContext The context in which the block search is performed, providing safe access to world data. * @param pos The position from which to start the block search. @@ -26,121 +26,62 @@ annotation class BlockDslMarker * ### Usage Example: * * ```kotlin - * val blocks = blockSearch { - * range(10.0) // Search for blocks within a 10 block radius - * step(2.0) // Check for blocks every 2 block - * filter { _, block -> block.isOf(Blocks.DIAMOND_BLOCK) } // Filter out blocks that are not diamond blocks - * iterator { pos, state -> println("Found diamond block at: $pos") } // Print the position of each diamond block - * }.build() // Finalize the block search + * val blocks = blockSearch(range = Vec3i(10, 10, 10)) { + * it.isOf(Blocks.DIAMOND_BLOCK) // Filter out blocks that are not diamond blocks + * } * - * println("Found ${blocks.size} diamond blocks.") + * blocks.forEach { (pos, state) -> + * println("Found diamond block at: $pos") + * } * ``` */ @BlockDslMarker class BlockDsl( private val safeContext: SafeContext, pos: BlockPos, + private val range: Vec3i, + private val step: Vec3i, + private val predicate: (BlockPos, BlockState) -> Boolean ) { private val fastVector = pos.toFastVec() private val receiver: MutableMap = mutableMapOf() - private var range: FastVector = MAGICVECTOR times 8 - private var step: FastVector = MAGICVECTOR - private var predicate: (FastVector, BlockState) -> Boolean = { _, _ -> true } - private var iterator: (FastVector, BlockState) -> Unit = { _, _ -> } - - /** - * Sets the vector representing the maximum distances from the position to search for blocks. - */ - fun range(range: Int): BlockDsl { - this.range = fastVectorOf(range, range, range) - return this - } - - /** - * Sets the vector representing the maximum distances from the position to search for blocks. - */ - fun range(range: Double): BlockDsl { - this.range = fastVectorOf(range.toInt(), range.toInt(), range.toInt()) - return this - } - - /** - * Sets the vector representing the maximum distances from the position to search for blocks. - */ - fun range(range: Vec3i): BlockDsl { - this.range = range.toFastVec() - return this - } - - /** - * Sets the vector representing the maximum distances from the position to search for blocks. - */ - fun range(range: Vec3d): BlockDsl { - this.range = range.toFastVec() - return this - } - - /** - * Sets the vector representing the intervals at which to check for blocks. - */ - fun step(step: Int): BlockDsl { - this.step = fastVectorOf(step, step, step) - return this - } - - /** - * Sets the vector representing the intervals at which to check for blocks. - */ - fun step(step: Double): BlockDsl { - this.step = fastVectorOf(step.toInt(), step.toInt(), step.toInt()) - return this - } - - /** - * Sets the vector representing the intervals at which to check for blocks. - */ - fun step(step: Vec3i): BlockDsl { - this.step = step.toFastVec() - return this - } - - /** - * Sets the vector representing the intervals at which to check for blocks. - */ - fun step(step: Vec3d): BlockDsl { - this.step = step.toFastVec() - return this - } - - /** - * Sets a predicate to filter blocks. - */ - fun filter(predicate: (BlockPos, BlockState) -> Boolean): BlockDsl { - this.predicate = { pos, state -> predicate(pos.toBlockPos(), state) } - return this - } - - /** - * Sets an iterator to perform operations on each block. - */ - fun iterator(iterator: (BlockPos, BlockState) -> Unit): BlockDsl { - this.iterator = { pos, state -> iterator(pos.toBlockPos(), state) } - return this - } - fun build(): Map { - safeContext.internalSearchBlocks(fastVector, range, step, receiver, predicate, iterator) + safeContext.internalSearchBlocks(fastVector, range.toFastVec(), step.toFastVec(), receiver, { pos, state -> predicate(pos.toBlockPos(), state) }, { _, _ -> }) return receiver.mapKeys { it.key.toBlockPos() } } } /** * Searches for blocks around the player's position and applies the specified block operations. - * Don't forget to call [BlockDsl.build] to finalize the search. * - * @param pos The position around which to search for blocks. - * @param block The block operations to apply. + * @param pos The position around which to search for blocks. Defaults to the player's current position. + * @param range The `x`, `y`, `z` range around the position to search for blocks. + * @param step The `x`, `y`, `z` step intervals at which to check for blocks. + * @param predicate The predicate to filter blocks. + * @return A map of block positions and their states matching the predicate within the specified range. */ -fun SafeContext.blockSearch(pos: BlockPos = player.blockPos, block: (@BlockDslMarker BlockDsl).() -> Unit) = BlockDsl(this, pos).apply(block) +fun SafeContext.blockSearch( + range: Vec3i, + step: Vec3i, + pos: BlockPos = player.blockPos, + predicate: (BlockPos, BlockState) -> Boolean +): Map = + BlockDsl(this, pos, range, step, predicate).build() +/** + * Searches for blocks around the player's position and applies the specified block operations. + * + * @param pos The position around which to search for blocks. Defaults to the player's current position. + * @param range The range around the position to search for blocks. + * @param step The step intervals at which to check for blocks. + * @param predicate The predicate to filter blocks. + * @return A map of block positions and their states matching the predicate within the specified range. + */ +fun SafeContext.blockSearch( + range: Int, + step: Int, + pos: BlockPos = player.blockPos, + predicate: (BlockPos, BlockState) -> Boolean +): Map = + blockSearch(Vec3i(range, range, range), Vec3i(step, step, step), pos, predicate) \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/world/EntityDsl.kt b/common/src/main/kotlin/com/lambda/util/world/EntityDsl.kt index fc53744a3..4af32e9da 100644 --- a/common/src/main/kotlin/com/lambda/util/world/EntityDsl.kt +++ b/common/src/main/kotlin/com/lambda/util/world/EntityDsl.kt @@ -10,39 +10,30 @@ import net.minecraft.entity.Entity import net.minecraft.util.math.BlockPos import kotlin.reflect.KClass -@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE) @DslMarker annotation class EntityDslMarker /** - * The `EntityDsl` class provides a DSLfor performing entity search operations - * within a specified range in a Minecraft world. It allows for filtering, iterating, and comparing entities + * The [EntityDsl] class provides a DSL for performing entity search operations + * within a specified range in a Minecraft world. It allows for filtering entities * of a specific type [T] around a given position. * * @param safeContext The context in which the entity search is performed, providing safe access to world data. * @param kClass The class of the entity type [T]. * @param pos The position from which to start the entity search. + * @param range The range around the position to search for entities. + * @param predicate The predicate to filter entities. * * ### Usage Example: * * ```kotlin - * val entities = entitySearch(player.blockPos) { - * range(10.0) // Search for entities within a 10 block radius - * filter { it.isAlive } // Filter out dead entities - * iterator { println("Found entity: ${it.name.string}") } // Print the name of each entity - * }.build() // Finalize the entity search, you can also use `buildFast` for optimized performances. + * val entities = entitySearch(range = 10.0) { + * it.isAlive // Filter out dead entities + * } * - * println("Found ${entities.size} entities.") - * ``` - * - * ```kotlin - * val closestEntityToFOV = entitySearch { - * range(10.0) // Search for entities within a 10 block radius - * filter { it.isAlive } // Filter out dead entities - * comparator { player.rotation dist player.eyePos.rotationTo(it.pos) } // Compare entities based on their distance to the player's field of view - * }.minBy() // Finalize the entity search and return the entity with the smallest value based on the comparator - * - * println("Closest entity to FOV: ${closestEntityToFOV}") + * val closestEntityToFOV = entities.minBy { + * player.rotation dist player.eyePos.rotationTo(it.pos) + * } * ``` */ @EntityDslMarker @@ -50,81 +41,19 @@ class EntityDsl( private val safeContext: SafeContext, private val kClass: KClass, pos: BlockPos, + private val range: Double, + private val predicate: (T) -> Boolean ) { private val fastVector = pos.toFastVec() private val receiver: MutableList = mutableListOf() - private var range: Double = 8.0 - private var predicate: (T) -> Boolean = { true } - private var iterator: (T) -> Unit = { _ -> } - private var comparator: SafeContext.(T) -> Double = { 0.0 } - - /** - * Sets the range around the position to search for entities. - */ - fun range(range: Int): EntityDsl { - this.range = range.toDouble() - return this - } - - /** - * Sets the range around the position to search for entities. - */ - fun range(range: Double): EntityDsl { - this.range = range - return this - } - - /** - * Sets a predicate to filter entities. - */ - fun filter(predicate: (T) -> Boolean): EntityDsl { - this.predicate = predicate - return this - } - - /** - * Sets an iterator to perform operations on each entity. - */ - fun iterator(iterator: (T) -> Unit): EntityDsl { - this.iterator = iterator - return this - } - - /** - * Sets a comparator to compare entities. - */ - fun comparator(comparator: SafeContext.(T) -> Double): EntityDsl { - this.comparator = comparator - return this - } - - /** - * Returns the entity that has the smallest value based on the given comparator. - * If multiple entities have the same value, the first one found will be returned. - * @return The entity with the smallest value based on the comparator, or null if no entity is found. - */ - fun minBy(): T? { - var min: T? = null - var minValue = Double.MAX_VALUE - - buildFast().forEach { entity -> - val value = safeContext.comparator(entity) - if (value < minValue) { - min = entity - minValue = value - } - } - - return min - } - /** * Retrieves a list of entities of type [T] within the specified range. * This method is safe to use with mutable lists and should be used when the optimized method is not suitable. */ + @EntityDslMarker fun build(): List { - safeContext.internalGetEntities(kClass, fastVector, range, receiver, predicate, iterator) + safeContext.internalGetEntities(kClass, fastVector, range, receiver, predicate) return receiver } @@ -132,17 +61,39 @@ class EntityDsl( * Retrieves a list of entities of type [T] within the specified range. * This method is optimized for performance and should be used when possible. */ + @EntityDslMarker fun buildFast(): List { - safeContext.internalGetFastEntities(kClass, fastVector, range, receiver, predicate, iterator) + safeContext.internalGetFastEntities(kClass, fastVector, range, receiver, predicate) return receiver } } /** * Initiates an entity search operation in the world at the specified position using an [EntityDsl]. - * Don't forget to call [EntityDsl.build] or [EntityDsl.buildFast] to finalize the search. * * @param pos The position to start the search from. Defaults to the player's current position. - * @param block The block of code that performs the search using the [EntityDsl]. + * @param range The range around the position to search for entities. + * @param predicate The predicate to filter entities. + * @return A list of entities matching the predicate within the specified range. */ -inline fun SafeContext.entitySearch(pos: BlockPos = player.blockPos, block: (@EntityDslMarker EntityDsl).() -> Unit) = EntityDsl(this, T::class, pos).apply(block) +@EntityDslMarker +inline fun SafeContext.entitySearch( + range: Double, + pos: BlockPos = player.blockPos, + noinline predicate: (T) -> Boolean = { true } +): List = EntityDsl(this, T::class, pos, range, predicate).build() + +/** + * Initiates an optimized entity search operation in the world at the specified position using an [EntityDsl]. + * + * @param pos The position to start the search from. Defaults to the player's current position. + * @param range The range around the position to search for entities. + * @param predicate The predicate to filter entities. + * @return A list of entities matching the predicate within the specified range. + */ +@EntityDslMarker +inline fun SafeContext.fastEntitySearch( + range: Double, + pos: BlockPos = player.blockPos, + noinline predicate: (T) -> Boolean +): List = EntityDsl(this, T::class, pos, range, predicate).buildFast() diff --git a/common/src/main/kotlin/com/lambda/util/world/FluidDsl.kt b/common/src/main/kotlin/com/lambda/util/world/FluidDsl.kt index 32a8a3e2f..29dfa2f5e 100644 --- a/common/src/main/kotlin/com/lambda/util/world/FluidDsl.kt +++ b/common/src/main/kotlin/com/lambda/util/world/FluidDsl.kt @@ -18,23 +18,27 @@ import kotlin.reflect.KClass annotation class FluidDslMarker /** - * The `FluidDsl` class provides a DSL for performing fluid search operations - * within a specified range in a Minecraft world. It allows for filtering and iterating fluids - * of a specific type [T] around a given position. + * The [FluidDsl] class provides a DSL for performing fluid search operations + * within a specified range in a Minecraft world. It allows for filtering fluids + * around a given position. * * @param safeContext The context in which the fluid search is performed, providing safe access to world data. * @param kClass The class of the fluid type [T]. * @param pos The position from which to start the fluid search. + * @param range The range around the position to search for fluids. + * @param step The step intervals at which to check for fluids. + * @param predicate The predicate to filter fluids. * * ### Usage Example: * * ```kotlin - * val fluids = fluidSearch { - * range(8) // Search for fluids within an 8 block radius - * iterator { pos, state -> println("Found immobile lava at $pos with state $state") } - * }.build() // Finalize the fluid search + * val fluids = fluidSearch(range = 8) { // Search for fluids within an 8 block radius + * it.isOf(Fluids.LAVA) // Filter out fluids that are not lava + * } * - * println("Found ${fluids.size} immobile lava fluids.") + * fluids.forEach { (pos, state) -> + * println("Found still lava at $pos with state $state") + * } * ``` */ @FluidDslMarker @@ -42,110 +46,47 @@ class FluidDsl( private val safeContext: SafeContext, private val kClass: KClass, pos: BlockPos, + private val range: Vec3i, + private val step: Vec3i, + private val predicate: (BlockPos, FluidState) -> Boolean ) { private val fastVector = pos.toFastVec() private val receiver: MutableMap = mutableMapOf() - private var range: FastVector = MAGICVECTOR times 8 - private var step: FastVector = MAGICVECTOR - private var predicate: (FastVector, FluidState) -> Boolean = { _, _ -> true } - private var iterator: (FastVector, FluidState) -> Unit = { _, _ -> } - - /** - * Sets the range around the position to search for fluids. - */ - fun range(range: Double): FluidDsl { - this.range = fastVectorOf(range.toInt(), range.toInt(), range.toInt()) - return this - } - - /** - * Sets the range around the position to search for fluids. - */ - fun range(range: Int): FluidDsl { - this.range = fastVectorOf(range, range, range) - return this - } - - /** - * Sets the range around the position to search for fluids. - */ - fun range(range: Vec3i): FluidDsl { - this.range = range.toFastVec() - return this - } - - /** - * Sets the range around the position to search for fluids. - */ - fun range(range: Vec3d): FluidDsl { - this.range = range.toFastVec() - return this - } - - /** - * Sets the vector representing the intervals at which to check for blocks. - */ - fun step(step: Int): FluidDsl { - this.step = fastVectorOf(step, step, step) - return this - } - - /** - * Sets the vector representing the intervals at which to check for blocks. - */ - fun step(step: Double): FluidDsl { - this.step = fastVectorOf(step.toInt(), step.toInt(), step.toInt()) - return this - } - - /** - * Sets the vector representing the intervals at which to check for blocks. - */ - fun step(step: Vec3i): FluidDsl { - this.step = step.toFastVec() - return this - } - - /** - * Sets the vector representing the intervals at which to check for blocks. - */ - fun step(step: Vec3d): FluidDsl { - this.step = step.toFastVec() - return this - } - - /** - * Sets a predicate to filter fluids. - */ - fun filter(predicate: (BlockPos, FluidState) -> Boolean): FluidDsl { - this.predicate = { pos, state -> predicate(pos.toBlockPos(), state) } - return this - } - - /** - * Sets an iterator to perform operations on each fluid. - */ - fun iterator(iterator: (BlockPos, FluidState) -> Unit): FluidDsl { - this.iterator = { pos, state -> iterator(pos.toBlockPos(), state) } - return this - } - - /** - * Builds the map of fluids found in the world. - */ fun build(): Map { - safeContext.internalSearchFluids(kClass, fastVector, range, step, receiver, predicate, iterator) - + safeContext.internalSearchFluids(kClass, fastVector, range.toFastVec(), step.toFastVec(), receiver, { pos, state -> predicate(pos.toBlockPos(), state) }, { _, _ -> }) return receiver.mapKeys { it.key.toBlockPos() } } } /** - * Initiates a fluid search operation in the world at the specified position using a [FluidDsl]. - * The fluid search operation is performed using the specified block of code. + * Searches for fluids around the player's position and applies the specified fluid operations. + * + * @param pos The position around which to search for fluids. Defaults to the player's current position. + * @param range The `x`, `y`, `z` range around the position to search for fluids. + * @param step The `x`, `y`, `z` step intervals at which to check for fluids. + * @param predicate The predicate to filter fluids. + * @return A map of fluid positions and their states matching the predicate within the specified range. + */ +inline fun SafeContext.fluidSearch( + range: Vec3i, + step: Vec3i, + pos: BlockPos = player.blockPos, + noinline predicate: (BlockPos, FluidState) -> Boolean +): Map = FluidDsl(this, T::class, pos, range, step, predicate).build() + +/** + * Searches for fluids around the player's position and applies the specified fluid operations. * - * @param pos The position to start the search from. Defaults to the player's current position. - * @param block The block of code that performs the search using the [FluidDsl]. + * @param pos The position around which to search for fluids. Defaults to the player's current position. + * @param range The range around the position to search for fluids. + * @param step The step intervals at which to check for fluids. + * @param predicate The predicate to filter fluids. + * @return A map of fluid positions and their states matching the predicate within the specified range. */ -inline fun SafeContext.fluidSearch(pos: BlockPos = player.blockPos, block: (@FluidDslMarker FluidDsl).() -> Unit) = FluidDsl(this, T::class, pos).apply(block) +inline fun SafeContext.fluidSearch( + range: Int, + step: Int, + pos: BlockPos = player.blockPos, + noinline predicate: (BlockPos, FluidState) -> Boolean +): Map = fluidSearch(Vec3i(range, range, range), Vec3i(step, step, step), pos, predicate) \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt index b4460b0ac..542a89111 100644 --- a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt @@ -18,7 +18,7 @@ import kotlin.reflect.KClass * * This object employs a pass-by-reference model, allowing functions to modify * data structures passed to them rather than creating new ones. This approach - * offers 2 main benefits: + * offers two main benefits: * * - **Performance**: Pass-by-reference avoids unnecessary memory allocations * and reallocations that can occur when creating new data structures. @@ -29,7 +29,7 @@ import kotlin.reflect.KClass * temporary objects. * * When you create a new object, the JVM allocates memory for it on the heap. - * When it is no longer needed, the garbage collector frees up the memory. + * When it is no longer necessary, the garbage collector frees up the memory. * This process **IS** expensive, especially if you are creating and discarding many objects * * Please note that the author of this code currently does not have any certifications in the field of computer science. @@ -171,7 +171,7 @@ object WorldUtils { iterator: (FastVector, BlockState) -> Unit = { _, _ -> }, ) { internalIteratePositions(pos, range, step) { position -> - world.getBlockState(position.x, position.y, position.z).let { state -> + world.getBlockState(position).let { state -> val fulfilled = predicate(position, state) if (fulfilled && pointer != null) {