From 94118252f128b0ae58984bafef733fac5a92064f Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Wed, 8 Jan 2025 15:40:56 -0500 Subject: [PATCH 1/4] feat: storage esp --- .../module/modules/render/StorageESP.kt | 173 ++++++++++++++++++ .../kotlin/com/lambda/util/extension/World.kt | 2 + 2 files changed, 175 insertions(+) create mode 100644 common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt b/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt new file mode 100644 index 000000000..24ed49156 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt @@ -0,0 +1,173 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.module.modules.render + +import com.lambda.context.SafeContext +import com.lambda.event.events.RenderEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.graphics.renderer.esp.DirectionMask +import com.lambda.graphics.renderer.esp.DirectionMask.buildSideMesh +import com.lambda.graphics.renderer.esp.builders.buildFilled +import com.lambda.graphics.renderer.esp.builders.buildFilledMesh +import com.lambda.graphics.renderer.esp.builders.buildOutline +import com.lambda.graphics.renderer.esp.builders.buildOutlineMesh +import com.lambda.graphics.renderer.esp.impl.StaticESPRenderer +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.threading.runSafe +import com.lambda.util.extension.blockColor +import com.lambda.util.extension.blockFilledMesh +import com.lambda.util.extension.blockOutlineMesh +import com.lambda.util.math.setAlpha +import com.lambda.util.world.blockEntitySearch +import com.lambda.util.world.entitySearch +import net.minecraft.block.entity.BarrelBlockEntity +import net.minecraft.block.entity.BlastFurnaceBlockEntity +import net.minecraft.block.entity.BlockEntity +import net.minecraft.block.entity.BrewingStandBlockEntity +import net.minecraft.block.entity.ChestBlockEntity +import net.minecraft.block.entity.DispenserBlockEntity +import net.minecraft.block.entity.EnderChestBlockEntity +import net.minecraft.block.entity.FurnaceBlockEntity +import net.minecraft.block.entity.HopperBlockEntity +import net.minecraft.block.entity.ShulkerBoxBlockEntity +import net.minecraft.block.entity.SmokerBlockEntity +import net.minecraft.entity.Entity +import net.minecraft.entity.decoration.ItemFrameEntity +import net.minecraft.entity.vehicle.AbstractMinecartEntity +import net.minecraft.util.math.BlockPos +import java.awt.Color + +object StorageESP : Module( + name = "StorageESP", + description = "Render storage blocks/entities", + defaultTags = setOf(ModuleTag.RENDER), +) { + private val page by setting("Page", Page.Render) + + /* General settings */ + private val distance by setting("Distance", 64.0, 10.0..256.0, 1.0, "Maximum distance for rendering") { page == Page.General } + + /* Render settings */ + private var drawFaces: Boolean by setting("Draw Faces", true, "Draw faces of blocks") { page == Page.Render }.apply { onValueSet { _, to -> if (!to) drawOutlines = true } } + private var drawOutlines: Boolean by setting("Draw Outlines", true, "Draw outlines of blocks") { page == Page.Render }.apply { onValueSet { _, to -> if (!to) drawFaces = true } } + private val outlineMode by setting("Outline Mode", DirectionMask.OutlineMode.AND, "Outline mode") { page == Page.Render } + private val mesh by setting("Mesh", true, "Connect similar adjacent blocks") + + /* Color settings */ + private val useBlockColor by setting("Use Block Color", true, "Use the color of the block instead") { page == Page.Color } + private val alpha by setting("Alpha", 0.3, 0.1..1.0, 0.05) { page == Page.Color } + + // TODO: + // Once we have map setting we can do this: + // val blockColors by setting("Block Colors", mapOf()) { page == Page.Color && !useBlockColor } + // val renders by setting("Block Colors", mapOf()) { page == Page.Color && !useBlockColor } + // + // TODO: Create enum of MapColors + + // I used this to extract the colors as rgb format + //> function extract(color) { + // ... console.log((color >> 16) & 0xFF) + // ... console.log((color >> 8) & 0xFF) + // ... console.log(color & 0xFF) + // ... } + + private val barrelColor by setting("Barrel Color", Color(143, 119, 72)) { page == Page.Color && !useBlockColor } + private val blastFurnaceColor by setting("Blast Furnace Color", Color(153, 153, 153)) { page == Page.Color && !useBlockColor } + private val brewingStandColor by setting("Brewing Stand Color", Color(167, 167, 167)) + private val chestColor by setting("Chest Color", Color(216, 127, 51)) { page == Page.Color && !useBlockColor } + private val dispenserColor by setting("Dispenser Color", Color(153, 153, 153)) { page == Page.Color && !useBlockColor } + private val enderChestColor by setting("Ender Chest Color", Color(127, 63, 178)) { page == Page.Color && !useBlockColor } + private val furnaceColor by setting("Furnace Color", Color(153, 153, 153)) { page == Page.Color && !useBlockColor } + private val hopperColor by setting("Hopper Color", Color(76, 76, 76)) { page == Page.Color && !useBlockColor } + private val smokerColor by setting("Smoker Color", Color(112, 112, 112)) { page == Page.Color && !useBlockColor } + private val shulkerColor by setting("Shulker Color", Color(178, 76, 216)) { page == Page.Color && !useBlockColor } + private val itemFrameColor by setting("Item Frame Color", Color(216, 127, 51)) { page == Page.Color && !useBlockColor } + private val cartColor by setting("Cart Color", Color(102, 127, 51)) { page == Page.Color && !useBlockColor } + + init { + listen { event -> + blockEntitySearch(range = distance) + .forEach { event.renderer.build(it, it.pos, buildMesh(it.pos)) } + + (entitySearch(range = distance) + + entitySearch(range = distance)) + .forEach { event.renderer.build(it, DirectionMask.ALL) } + } + } + + private fun SafeContext.buildMesh(position: BlockPos) = + if (mesh) buildSideMesh(position) { + val block = world.getBlockEntity(it) ?: return@buildSideMesh false + + getBlockEntityColor(block) != null && + block.cachedState.isFullCube(world, it) + } + else DirectionMask.ALL + + private fun StaticESPRenderer.build( + block: BlockEntity, + pos: BlockPos, + sides: Int, + ) = runSafe { + val color = if (useBlockColor) blockColor(block.cachedState, pos) else getBlockEntityColor(block) ?: return@runSafe + val filledMesh = blockFilledMesh(block.cachedState, pos) + val outlineMesh = blockOutlineMesh(block.cachedState, pos) + + if (drawFaces) buildFilledMesh(filledMesh, color.setAlpha(alpha), sides) + if (drawOutlines) buildOutlineMesh(outlineMesh, color, sides, outlineMode) + } + + private fun StaticESPRenderer.build( + entity: Entity, + sides: Int, + ) = runSafe { + val color = getEntityColor(entity) ?: return@runSafe + + if (drawFaces) buildFilled(entity.boundingBox, color.setAlpha(alpha), sides) + if (drawOutlines) buildOutline(entity.boundingBox, color, sides, outlineMode) + } + + private fun getBlockEntityColor(block: BlockEntity?) = + when (block) { + is BarrelBlockEntity -> barrelColor + is BlastFurnaceBlockEntity -> blastFurnaceColor + is BrewingStandBlockEntity -> brewingStandColor + is ChestBlockEntity -> chestColor + is DispenserBlockEntity -> dispenserColor + is EnderChestBlockEntity -> enderChestColor + is FurnaceBlockEntity -> furnaceColor + is HopperBlockEntity -> hopperColor + is SmokerBlockEntity -> smokerColor + is ShulkerBoxBlockEntity -> shulkerColor + else -> null + } + + private fun getEntityColor(entity: Entity?) = + when (entity) { + is AbstractMinecartEntity -> cartColor + is ItemFrameEntity -> itemFrameColor + else -> null + } + + private enum class Page { + General, + Render, + Color + } +} diff --git a/common/src/main/kotlin/com/lambda/util/extension/World.kt b/common/src/main/kotlin/com/lambda/util/extension/World.kt index 623df5a66..c7ccaf227 100644 --- a/common/src/main/kotlin/com/lambda/util/extension/World.kt +++ b/common/src/main/kotlin/com/lambda/util/extension/World.kt @@ -24,6 +24,7 @@ import com.lambda.util.world.* import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.block.Blocks +import net.minecraft.block.entity.BlockEntity import net.minecraft.datafixer.DataFixTypes import net.minecraft.fluid.FluidState import net.minecraft.fluid.Fluids @@ -68,6 +69,7 @@ fun World.getFluidState(x: Int, y: Int, z: Int): FluidState { } fun World.getBlockState(vec: FastVector): BlockState = getBlockState(vec.x, vec.y, vec.z) +fun World.getBlockEntity(vec: FastVector) = getBlockEntity(vec.toBlockPos()) fun World.getFluidState(vec: FastVector): FluidState = getFluidState(vec.x, vec.y, vec.z) private fun positionFromIndex(width: Int, length: Int, index: Int): FastVector { From 1305be9ec7c18fdde8e3de73fa8cd2a95b4dd8a3 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Wed, 8 Jan 2025 15:45:37 -0500 Subject: [PATCH 2/4] comment --- .../main/kotlin/com/lambda/module/modules/render/StorageESP.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt b/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt index 24ed49156..e0e06003e 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt @@ -107,7 +107,7 @@ object StorageESP : Module( (entitySearch(range = distance) + entitySearch(range = distance)) - .forEach { event.renderer.build(it, DirectionMask.ALL) } + .forEach { event.renderer.build(it, DirectionMask.ALL) } // I didn't add block entity meshing because I'm not sure how to handle blocks that aren't full } } From 4f62d214774c9e993851905c5ca7bbfdb89ad053 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Wed, 8 Jan 2025 15:47:03 -0500 Subject: [PATCH 3/4] fixed todo comment --- .../main/kotlin/com/lambda/module/modules/render/StorageESP.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt b/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt index e0e06003e..56f7d3582 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt @@ -76,7 +76,7 @@ object StorageESP : Module( // TODO: // Once we have map setting we can do this: // val blockColors by setting("Block Colors", mapOf()) { page == Page.Color && !useBlockColor } - // val renders by setting("Block Colors", mapOf()) { page == Page.Color && !useBlockColor } + // val renders by setting("Render Blocks", mapOf()) { page == Page.General } // // TODO: Create enum of MapColors From 7043f43f2fdf18c781ad3b11575e00dd8d835c45 Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 9 Jan 2025 00:20:21 +0100 Subject: [PATCH 4/4] Rework mesh building and shape handling --- .../graphics/renderer/esp/DirectionMask.kt | 13 ++-- .../lambda/module/modules/render/BlockESP.kt | 10 ++- .../module/modules/render/StorageESP.kt | 71 ++++++++++++------- .../kotlin/com/lambda/util/extension/World.kt | 5 +- .../kotlin/com/lambda/util/world/WorldDsl.kt | 4 +- 5 files changed, 64 insertions(+), 39 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/DirectionMask.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/DirectionMask.kt index 43a84aa74..5f6c60746 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/DirectionMask.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/DirectionMask.kt @@ -19,8 +19,6 @@ package com.lambda.graphics.renderer.esp import com.lambda.util.world.FastVector import com.lambda.util.world.offset -import com.lambda.util.world.toBlockPos -import com.lambda.util.world.toFastVec import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction @@ -43,8 +41,15 @@ object DirectionMask { fun Int.exclude(direction: Direction) = exclude(direction.mask) fun Int.hasDirection(dir: Int) = (this and dir) != 0 - fun buildSideMesh(position: BlockPos, filter: (BlockPos) -> Boolean) = - buildSideMesh(position.toFastVec()) { filter(it.toBlockPos()) } + fun buildSideMesh(position: BlockPos, filter: (BlockPos) -> Boolean): Int { + var sides = ALL + + Direction.entries + .filter { filter(position.offset(it)) } + .forEach { sides = sides.exclude(it.mask) } + + return sides + } fun buildSideMesh(position: FastVector, filter: (FastVector) -> Boolean): Int { var sides = ALL diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt b/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt index 74d4930b7..09a849f2e 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt @@ -28,8 +28,7 @@ import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.threading.runSafe import com.lambda.util.extension.blockColor -import com.lambda.util.extension.blockFilledMesh -import com.lambda.util.extension.blockOutlineMesh +import com.lambda.util.extension.outlineShape import com.lambda.util.extension.getBlockState import com.lambda.util.world.fastVectorOf import com.lambda.util.world.toBlockPos @@ -91,12 +90,11 @@ object BlockESP : Module( pos: BlockPos, sides: Int, ) = runSafe { - val filledMesh = blockFilledMesh(state, pos) - val outlineMesh = blockOutlineMesh(state, pos) + val shape = outlineShape(state, pos) val blockColor = blockColor(state, pos) - if (drawFaces) buildFilledMesh(filledMesh, if (useBlockColor) blockColor else faceColor, sides) - if (drawOutlines) buildOutlineMesh(outlineMesh, if (useBlockColor) blockColor else outlineColor, sides, outlineMode) + if (drawFaces) buildFilledMesh(shape, if (useBlockColor) blockColor else faceColor, sides) + if (drawOutlines) buildOutlineMesh(shape, if (useBlockColor) blockColor else outlineColor, sides, outlineMode) } private fun rebuildMesh(from: Any, to: Any): Unit = esp.rebuild() diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt b/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt index 56f7d3582..5fac5a4de 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt @@ -31,8 +31,7 @@ import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.threading.runSafe import com.lambda.util.extension.blockColor -import com.lambda.util.extension.blockFilledMesh -import com.lambda.util.extension.blockOutlineMesh +import com.lambda.util.extension.outlineShape import com.lambda.util.math.setAlpha import com.lambda.util.world.blockEntitySearch import com.lambda.util.world.entitySearch @@ -50,6 +49,7 @@ import net.minecraft.block.entity.SmokerBlockEntity import net.minecraft.entity.Entity import net.minecraft.entity.decoration.ItemFrameEntity import net.minecraft.entity.vehicle.AbstractMinecartEntity +import net.minecraft.entity.vehicle.MinecartEntity import net.minecraft.util.math.BlockPos import java.awt.Color @@ -67,14 +67,13 @@ object StorageESP : Module( private var drawFaces: Boolean by setting("Draw Faces", true, "Draw faces of blocks") { page == Page.Render }.apply { onValueSet { _, to -> if (!to) drawOutlines = true } } private var drawOutlines: Boolean by setting("Draw Outlines", true, "Draw outlines of blocks") { page == Page.Render }.apply { onValueSet { _, to -> if (!to) drawFaces = true } } private val outlineMode by setting("Outline Mode", DirectionMask.OutlineMode.AND, "Outline mode") { page == Page.Render } - private val mesh by setting("Mesh", true, "Connect similar adjacent blocks") + private val mesh by setting("Mesh", true, "Connect similar adjacent blocks") { page == Page.Render } /* Color settings */ private val useBlockColor by setting("Use Block Color", true, "Use the color of the block instead") { page == Page.Color } private val alpha by setting("Alpha", 0.3, 0.1..1.0, 0.05) { page == Page.Color } // TODO: - // Once we have map setting we can do this: // val blockColors by setting("Block Colors", mapOf()) { page == Page.Color && !useBlockColor } // val renders by setting("Render Blocks", mapOf()) { page == Page.General } // @@ -98,39 +97,63 @@ object StorageESP : Module( private val smokerColor by setting("Smoker Color", Color(112, 112, 112)) { page == Page.Color && !useBlockColor } private val shulkerColor by setting("Shulker Color", Color(178, 76, 216)) { page == Page.Color && !useBlockColor } private val itemFrameColor by setting("Item Frame Color", Color(216, 127, 51)) { page == Page.Color && !useBlockColor } - private val cartColor by setting("Cart Color", Color(102, 127, 51)) { page == Page.Color && !useBlockColor } + private val cartColor by setting("Minecart Color", Color(102, 127, 51)) { page == Page.Color && !useBlockColor } + + private val entities = setOf( + BarrelBlockEntity::class, + BlastFurnaceBlockEntity::class, + BrewingStandBlockEntity::class, + ChestBlockEntity::class, + DispenserBlockEntity::class, + EnderChestBlockEntity::class, + FurnaceBlockEntity::class, + HopperBlockEntity::class, + SmokerBlockEntity::class, + ShulkerBoxBlockEntity::class, + AbstractMinecartEntity::class, + ItemFrameEntity::class, + MinecartEntity::class, + ) init { listen { event -> - blockEntitySearch(range = distance) - .forEach { event.renderer.build(it, it.pos, buildMesh(it.pos)) } - - (entitySearch(range = distance) + - entitySearch(range = distance)) - .forEach { event.renderer.build(it, DirectionMask.ALL) } // I didn't add block entity meshing because I'm not sure how to handle blocks that aren't full + blockEntitySearch(distance) + .filter { it::class in entities } + .forEach { event.renderer.build(it, it.pos, excludedSides(it)) } + + val mineCarts = entitySearch(distance) + val itemFrames = entitySearch(distance) + (mineCarts + itemFrames) + .forEach { event.renderer.build(it, DirectionMask.ALL) } } } - private fun SafeContext.buildMesh(position: BlockPos) = - if (mesh) buildSideMesh(position) { - val block = world.getBlockEntity(it) ?: return@buildSideMesh false - - getBlockEntityColor(block) != null && - block.cachedState.isFullCube(world, it) - } - else DirectionMask.ALL + private fun SafeContext.excludedSides(blockEntity: BlockEntity): Int { + val isFullCube = blockEntity.cachedState.isFullCube(world, blockEntity.pos) + return if (mesh && isFullCube) { + buildSideMesh(blockEntity.pos) { neighbor -> + val other = world.getBlockEntity(neighbor) ?: return@buildSideMesh false + val otherFullCube = other.cachedState.isFullCube(world, other.pos) + val sameType = blockEntity.cachedState.block == other.cachedState.block + val searchedFor = other::class in entities + + searchedFor && otherFullCube && sameType + } + } else DirectionMask.ALL + } private fun StaticESPRenderer.build( block: BlockEntity, pos: BlockPos, sides: Int, ) = runSafe { - val color = if (useBlockColor) blockColor(block.cachedState, pos) else getBlockEntityColor(block) ?: return@runSafe - val filledMesh = blockFilledMesh(block.cachedState, pos) - val outlineMesh = blockOutlineMesh(block.cachedState, pos) + val color = if (useBlockColor) { + blockColor(block.cachedState, pos) + } else getBlockEntityColor(block) ?: return@runSafe + val shape = outlineShape(block.cachedState, pos) - if (drawFaces) buildFilledMesh(filledMesh, color.setAlpha(alpha), sides) - if (drawOutlines) buildOutlineMesh(outlineMesh, color, sides, outlineMode) + if (drawFaces) buildFilledMesh(shape, color.setAlpha(alpha), sides) + if (drawOutlines) buildOutlineMesh(shape, color, sides, outlineMode) } private fun StaticESPRenderer.build( diff --git a/common/src/main/kotlin/com/lambda/util/extension/World.kt b/common/src/main/kotlin/com/lambda/util/extension/World.kt index c7ccaf227..de812386a 100644 --- a/common/src/main/kotlin/com/lambda/util/extension/World.kt +++ b/common/src/main/kotlin/com/lambda/util/extension/World.kt @@ -24,7 +24,6 @@ import com.lambda.util.world.* import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.block.Blocks -import net.minecraft.block.entity.BlockEntity import net.minecraft.datafixer.DataFixTypes import net.minecraft.fluid.FluidState import net.minecraft.fluid.Fluids @@ -37,10 +36,10 @@ import net.minecraft.world.World import java.awt.Color import kotlin.experimental.and -fun SafeContext.blockFilledMesh(state: BlockState, pos: BlockPos) = +fun SafeContext.collisionShape(state: BlockState, pos: BlockPos) = state.getCollisionShape(world, pos).offset(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) -fun SafeContext.blockOutlineMesh(state: BlockState, pos: BlockPos) = +fun SafeContext.outlineShape(state: BlockState, pos: BlockPos) = state.getOutlineShape(world, pos).offset(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) fun SafeContext.blockColor(state: BlockState, pos: BlockPos) = 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 aeb148d44..b37145069 100644 --- a/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt +++ b/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt @@ -124,7 +124,7 @@ inline fun SafeContext.blockEntitySearch( range: Double = 64.0, pos: BlockPos = player.blockPos, noinline filter: (T) -> Boolean = { true }, -) = internalGetBlockEntities(pos.toFastVec(), range, predicate = filter) +) = internalGetBlockEntities(pos.toFastVec(), range, predicate = filter).toSet() @DslMarker annotation class EntityMarker @@ -201,7 +201,7 @@ inline fun SafeContext.fastEntitySearch( range: Double, pos: BlockPos = player.blockPos, noinline filter: (T) -> Boolean = { true }, -) = internalGetFastEntities(pos.toFastVec(), range, predicate = filter) +) = internalGetFastEntities(pos.toFastVec(), range, predicate = filter).toSet() @DslMarker annotation class FluidMarker