Skip to content

Commit c650089

Browse files
emyfopsAvanatiker
andauthored
[1.20.4] Feat: StorageESP (#94)
This pull request adds a storage esp feature to render meshes of block entities that can store items in any shape or form I am waiting for the gui refactor in order to [simplify the settings](https://github.com/Avanatiker/NeoLambda/compare/master...feature/storageesp?expand=1#diff-1e422de9136597f0933501dc09780d9d3211d98f3560cd26350cc1921bdad4a3R76-R79), but this module is considered complete in my opinion ![image](https://github.com/user-attachments/assets/961b6e6e-d56e-4344-a3cc-827c5dc21903) --------- Co-authored-by: Constructor <fractalminds@protonmail.com>
1 parent a7ef8b7 commit c650089

File tree

5 files changed

+214
-14
lines changed

5 files changed

+214
-14
lines changed

common/src/main/kotlin/com/lambda/graphics/renderer/esp/DirectionMask.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ package com.lambda.graphics.renderer.esp
1919

2020
import com.lambda.util.world.FastVector
2121
import com.lambda.util.world.offset
22-
import com.lambda.util.world.toBlockPos
23-
import com.lambda.util.world.toFastVec
2422
import net.minecraft.util.math.BlockPos
2523
import net.minecraft.util.math.Direction
2624

@@ -43,8 +41,15 @@ object DirectionMask {
4341
fun Int.exclude(direction: Direction) = exclude(direction.mask)
4442
fun Int.hasDirection(dir: Int) = (this and dir) != 0
4543

46-
fun buildSideMesh(position: BlockPos, filter: (BlockPos) -> Boolean) =
47-
buildSideMesh(position.toFastVec()) { filter(it.toBlockPos()) }
44+
fun buildSideMesh(position: BlockPos, filter: (BlockPos) -> Boolean): Int {
45+
var sides = ALL
46+
47+
Direction.entries
48+
.filter { filter(position.offset(it)) }
49+
.forEach { sides = sides.exclude(it.mask) }
50+
51+
return sides
52+
}
4853

4954
fun buildSideMesh(position: FastVector, filter: (FastVector) -> Boolean): Int {
5055
var sides = ALL

common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ import com.lambda.module.Module
2828
import com.lambda.module.tag.ModuleTag
2929
import com.lambda.threading.runSafe
3030
import com.lambda.util.extension.blockColor
31-
import com.lambda.util.extension.blockFilledMesh
32-
import com.lambda.util.extension.blockOutlineMesh
31+
import com.lambda.util.extension.outlineShape
3332
import com.lambda.util.extension.getBlockState
3433
import com.lambda.util.world.fastVectorOf
3534
import com.lambda.util.world.toBlockPos
@@ -91,12 +90,11 @@ object BlockESP : Module(
9190
pos: BlockPos,
9291
sides: Int,
9392
) = runSafe {
94-
val filledMesh = blockFilledMesh(state, pos)
95-
val outlineMesh = blockOutlineMesh(state, pos)
93+
val shape = outlineShape(state, pos)
9694
val blockColor = blockColor(state, pos)
9795

98-
if (drawFaces) buildFilledMesh(filledMesh, if (useBlockColor) blockColor else faceColor, sides)
99-
if (drawOutlines) buildOutlineMesh(outlineMesh, if (useBlockColor) blockColor else outlineColor, sides, outlineMode)
96+
if (drawFaces) buildFilledMesh(shape, if (useBlockColor) blockColor else faceColor, sides)
97+
if (drawOutlines) buildOutlineMesh(shape, if (useBlockColor) blockColor else outlineColor, sides, outlineMode)
10098
}
10199

102100
private fun rebuildMesh(from: Any, to: Any): Unit = esp.rebuild()
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/*
2+
* Copyright 2025 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.lambda.module.modules.render
19+
20+
import com.lambda.context.SafeContext
21+
import com.lambda.event.events.RenderEvent
22+
import com.lambda.event.listener.SafeListener.Companion.listen
23+
import com.lambda.graphics.renderer.esp.DirectionMask
24+
import com.lambda.graphics.renderer.esp.DirectionMask.buildSideMesh
25+
import com.lambda.graphics.renderer.esp.builders.buildFilled
26+
import com.lambda.graphics.renderer.esp.builders.buildFilledMesh
27+
import com.lambda.graphics.renderer.esp.builders.buildOutline
28+
import com.lambda.graphics.renderer.esp.builders.buildOutlineMesh
29+
import com.lambda.graphics.renderer.esp.impl.StaticESPRenderer
30+
import com.lambda.module.Module
31+
import com.lambda.module.tag.ModuleTag
32+
import com.lambda.threading.runSafe
33+
import com.lambda.util.extension.blockColor
34+
import com.lambda.util.extension.outlineShape
35+
import com.lambda.util.math.setAlpha
36+
import com.lambda.util.world.blockEntitySearch
37+
import com.lambda.util.world.entitySearch
38+
import net.minecraft.block.entity.BarrelBlockEntity
39+
import net.minecraft.block.entity.BlastFurnaceBlockEntity
40+
import net.minecraft.block.entity.BlockEntity
41+
import net.minecraft.block.entity.BrewingStandBlockEntity
42+
import net.minecraft.block.entity.ChestBlockEntity
43+
import net.minecraft.block.entity.DispenserBlockEntity
44+
import net.minecraft.block.entity.EnderChestBlockEntity
45+
import net.minecraft.block.entity.FurnaceBlockEntity
46+
import net.minecraft.block.entity.HopperBlockEntity
47+
import net.minecraft.block.entity.ShulkerBoxBlockEntity
48+
import net.minecraft.block.entity.SmokerBlockEntity
49+
import net.minecraft.entity.Entity
50+
import net.minecraft.entity.decoration.ItemFrameEntity
51+
import net.minecraft.entity.vehicle.AbstractMinecartEntity
52+
import net.minecraft.entity.vehicle.MinecartEntity
53+
import net.minecraft.util.math.BlockPos
54+
import java.awt.Color
55+
56+
object StorageESP : Module(
57+
name = "StorageESP",
58+
description = "Render storage blocks/entities",
59+
defaultTags = setOf(ModuleTag.RENDER),
60+
) {
61+
private val page by setting("Page", Page.Render)
62+
63+
/* General settings */
64+
private val distance by setting("Distance", 64.0, 10.0..256.0, 1.0, "Maximum distance for rendering") { page == Page.General }
65+
66+
/* Render settings */
67+
private var drawFaces: Boolean by setting("Draw Faces", true, "Draw faces of blocks") { page == Page.Render }.apply { onValueSet { _, to -> if (!to) drawOutlines = true } }
68+
private var drawOutlines: Boolean by setting("Draw Outlines", true, "Draw outlines of blocks") { page == Page.Render }.apply { onValueSet { _, to -> if (!to) drawFaces = true } }
69+
private val outlineMode by setting("Outline Mode", DirectionMask.OutlineMode.AND, "Outline mode") { page == Page.Render }
70+
private val mesh by setting("Mesh", true, "Connect similar adjacent blocks") { page == Page.Render }
71+
72+
/* Color settings */
73+
private val useBlockColor by setting("Use Block Color", true, "Use the color of the block instead") { page == Page.Color }
74+
private val alpha by setting("Alpha", 0.3, 0.1..1.0, 0.05) { page == Page.Color }
75+
76+
// TODO:
77+
// val blockColors by setting("Block Colors", mapOf<String, Color>()) { page == Page.Color && !useBlockColor }
78+
// val renders by setting("Render Blocks", mapOf<String, Boolean>()) { page == Page.General }
79+
//
80+
// TODO: Create enum of MapColors
81+
82+
// I used this to extract the colors as rgb format
83+
//> function extract(color) {
84+
// ... console.log((color >> 16) & 0xFF)
85+
// ... console.log((color >> 8) & 0xFF)
86+
// ... console.log(color & 0xFF)
87+
// ... }
88+
89+
private val barrelColor by setting("Barrel Color", Color(143, 119, 72)) { page == Page.Color && !useBlockColor }
90+
private val blastFurnaceColor by setting("Blast Furnace Color", Color(153, 153, 153)) { page == Page.Color && !useBlockColor }
91+
private val brewingStandColor by setting("Brewing Stand Color", Color(167, 167, 167))
92+
private val chestColor by setting("Chest Color", Color(216, 127, 51)) { page == Page.Color && !useBlockColor }
93+
private val dispenserColor by setting("Dispenser Color", Color(153, 153, 153)) { page == Page.Color && !useBlockColor }
94+
private val enderChestColor by setting("Ender Chest Color", Color(127, 63, 178)) { page == Page.Color && !useBlockColor }
95+
private val furnaceColor by setting("Furnace Color", Color(153, 153, 153)) { page == Page.Color && !useBlockColor }
96+
private val hopperColor by setting("Hopper Color", Color(76, 76, 76)) { page == Page.Color && !useBlockColor }
97+
private val smokerColor by setting("Smoker Color", Color(112, 112, 112)) { page == Page.Color && !useBlockColor }
98+
private val shulkerColor by setting("Shulker Color", Color(178, 76, 216)) { page == Page.Color && !useBlockColor }
99+
private val itemFrameColor by setting("Item Frame Color", Color(216, 127, 51)) { page == Page.Color && !useBlockColor }
100+
private val cartColor by setting("Minecart Color", Color(102, 127, 51)) { page == Page.Color && !useBlockColor }
101+
102+
private val entities = setOf(
103+
BarrelBlockEntity::class,
104+
BlastFurnaceBlockEntity::class,
105+
BrewingStandBlockEntity::class,
106+
ChestBlockEntity::class,
107+
DispenserBlockEntity::class,
108+
EnderChestBlockEntity::class,
109+
FurnaceBlockEntity::class,
110+
HopperBlockEntity::class,
111+
SmokerBlockEntity::class,
112+
ShulkerBoxBlockEntity::class,
113+
AbstractMinecartEntity::class,
114+
ItemFrameEntity::class,
115+
MinecartEntity::class,
116+
)
117+
118+
init {
119+
listen<RenderEvent.StaticESP> { event ->
120+
blockEntitySearch<BlockEntity>(distance)
121+
.filter { it::class in entities }
122+
.forEach { event.renderer.build(it, it.pos, excludedSides(it)) }
123+
124+
val mineCarts = entitySearch<AbstractMinecartEntity>(distance)
125+
val itemFrames = entitySearch<ItemFrameEntity>(distance)
126+
(mineCarts + itemFrames)
127+
.forEach { event.renderer.build(it, DirectionMask.ALL) }
128+
}
129+
}
130+
131+
private fun SafeContext.excludedSides(blockEntity: BlockEntity): Int {
132+
val isFullCube = blockEntity.cachedState.isFullCube(world, blockEntity.pos)
133+
return if (mesh && isFullCube) {
134+
buildSideMesh(blockEntity.pos) { neighbor ->
135+
val other = world.getBlockEntity(neighbor) ?: return@buildSideMesh false
136+
val otherFullCube = other.cachedState.isFullCube(world, other.pos)
137+
val sameType = blockEntity.cachedState.block == other.cachedState.block
138+
val searchedFor = other::class in entities
139+
140+
searchedFor && otherFullCube && sameType
141+
}
142+
} else DirectionMask.ALL
143+
}
144+
145+
private fun StaticESPRenderer.build(
146+
block: BlockEntity,
147+
pos: BlockPos,
148+
sides: Int,
149+
) = runSafe {
150+
val color = if (useBlockColor) {
151+
blockColor(block.cachedState, pos)
152+
} else getBlockEntityColor(block) ?: return@runSafe
153+
val shape = outlineShape(block.cachedState, pos)
154+
155+
if (drawFaces) buildFilledMesh(shape, color.setAlpha(alpha), sides)
156+
if (drawOutlines) buildOutlineMesh(shape, color, sides, outlineMode)
157+
}
158+
159+
private fun StaticESPRenderer.build(
160+
entity: Entity,
161+
sides: Int,
162+
) = runSafe {
163+
val color = getEntityColor(entity) ?: return@runSafe
164+
165+
if (drawFaces) buildFilled(entity.boundingBox, color.setAlpha(alpha), sides)
166+
if (drawOutlines) buildOutline(entity.boundingBox, color, sides, outlineMode)
167+
}
168+
169+
private fun getBlockEntityColor(block: BlockEntity?) =
170+
when (block) {
171+
is BarrelBlockEntity -> barrelColor
172+
is BlastFurnaceBlockEntity -> blastFurnaceColor
173+
is BrewingStandBlockEntity -> brewingStandColor
174+
is ChestBlockEntity -> chestColor
175+
is DispenserBlockEntity -> dispenserColor
176+
is EnderChestBlockEntity -> enderChestColor
177+
is FurnaceBlockEntity -> furnaceColor
178+
is HopperBlockEntity -> hopperColor
179+
is SmokerBlockEntity -> smokerColor
180+
is ShulkerBoxBlockEntity -> shulkerColor
181+
else -> null
182+
}
183+
184+
private fun getEntityColor(entity: Entity?) =
185+
when (entity) {
186+
is AbstractMinecartEntity -> cartColor
187+
is ItemFrameEntity -> itemFrameColor
188+
else -> null
189+
}
190+
191+
private enum class Page {
192+
General,
193+
Render,
194+
Color
195+
}
196+
}

common/src/main/kotlin/com/lambda/util/extension/World.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ import net.minecraft.world.World
3636
import java.awt.Color
3737
import kotlin.experimental.and
3838

39-
fun SafeContext.blockFilledMesh(state: BlockState, pos: BlockPos) =
39+
fun SafeContext.collisionShape(state: BlockState, pos: BlockPos) =
4040
state.getCollisionShape(world, pos).offset(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble())
4141

42-
fun SafeContext.blockOutlineMesh(state: BlockState, pos: BlockPos) =
42+
fun SafeContext.outlineShape(state: BlockState, pos: BlockPos) =
4343
state.getOutlineShape(world, pos).offset(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble())
4444

4545
fun SafeContext.blockColor(state: BlockState, pos: BlockPos) =
@@ -68,6 +68,7 @@ fun World.getFluidState(x: Int, y: Int, z: Int): FluidState {
6868
}
6969

7070
fun World.getBlockState(vec: FastVector): BlockState = getBlockState(vec.x, vec.y, vec.z)
71+
fun World.getBlockEntity(vec: FastVector) = getBlockEntity(vec.toBlockPos())
7172
fun World.getFluidState(vec: FastVector): FluidState = getFluidState(vec.x, vec.y, vec.z)
7273

7374
private fun positionFromIndex(width: Int, length: Int, index: Int): FastVector {

common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ inline fun <reified T : BlockEntity> SafeContext.blockEntitySearch(
124124
range: Double = 64.0,
125125
pos: BlockPos = player.blockPos,
126126
noinline filter: (T) -> Boolean = { true },
127-
) = internalGetBlockEntities<T>(pos.toFastVec(), range, predicate = filter)
127+
) = internalGetBlockEntities<T>(pos.toFastVec(), range, predicate = filter).toSet()
128128

129129
@DslMarker
130130
annotation class EntityMarker
@@ -201,7 +201,7 @@ inline fun <reified T : Entity> SafeContext.fastEntitySearch(
201201
range: Double,
202202
pos: BlockPos = player.blockPos,
203203
noinline filter: (T) -> Boolean = { true },
204-
) = internalGetFastEntities<T>(pos.toFastVec(), range, predicate = filter)
204+
) = internalGetFastEntities<T>(pos.toFastVec(), range, predicate = filter).toSet()
205205

206206
@DslMarker
207207
annotation class FluidMarker

0 commit comments

Comments
 (0)