2525import net .minecraft .util .math .ChunkPos ;
2626import net .minecraft .world .chunk .Chunk ;
2727import net .minecraft .world .dimension .DimensionType ;
28+ import net .minecraft .block .BlockState ;
29+ import net .minecraft .state .property .Property ;
2830
2931import java .util .Iterator ;
3032import java .util .List ;
3133import java .util .Map ;
3234import java .util .Set ;
3335import java .util .concurrent .ExecutorService ;
3436import java .util .concurrent .Executors ;
37+ import java .util .HashMap ;
38+ import java .util .Optional ;
3539
3640public class BlockESP extends Module {
3741 private final SettingGroup sgGeneral = settings .getDefaultGroup ();
3842
3943 // General
4044
41- private final Setting <List <Block >> blocks = sgGeneral .add (new BlockListSetting .Builder ()
42- .name ("blocks" )
43- .description ("Blocks to search for." )
44- .onChanged (blocks1 -> {
45- if (isActive () && Utils .canUpdate ()) onActivate ();
46- })
47- .build ()
45+ private final Setting <List <Block >> blocks = sgGeneral .add (
46+ new BlockListSetting .Builder ()
47+ .name ("blocks" )
48+ .description ("Blocks to search for." )
49+ .onChanged (blocks1 -> {
50+ if (isActive () && Utils .canUpdate ()) onActivate ();
51+ })
52+ .build ()
4853 );
49-
50- private final Setting <ESPBlockData > defaultBlockConfig = sgGeneral .add (new GenericSetting .Builder <ESPBlockData >()
51- .name ("default-block-config" )
52- .description ("Default block config." )
53- .defaultValue (
54- new ESPBlockData (
55- ShapeMode .Lines ,
56- new SettingColor (0 , 255 , 200 ),
57- new SettingColor (0 , 255 , 200 , 25 ),
58- true ,
59- new SettingColor (0 , 255 , 200 , 125 )
54+
55+ // uncomment if NBT-Data should apply to all blocks !!! UNTESTED !!!
56+
57+ /*
58+ private final Setting<List<String>> customFilters = sgGeneral.add(
59+ new StringListSetting.Builder()
60+ .name("NTB-Data")
61+ .description(
62+ "Filters with ntbdata (e.g. 'waterlogged=true')."
6063 )
61- )
62- .build ()
64+ .defaultValue(new ArrayList<>())
65+ .onChanged(this::parseFilters)
66+ .build()
6367 );
64-
65- private final Setting <Map <Block , ESPBlockData >> blockConfigs = sgGeneral .add (new BlockDataSetting .Builder <ESPBlockData >()
66- .name ("block-configs" )
67- .description ("Config for each block." )
68- .defaultData (defaultBlockConfig )
69- .build ()
68+ */
69+
70+ private final Setting <ESPBlockData > defaultBlockConfig = sgGeneral .add (
71+ new GenericSetting .Builder <ESPBlockData >()
72+ .name ("default-block-config" )
73+ .description ("Default block config." )
74+ .defaultValue (
75+ new ESPBlockData (
76+ ShapeMode .Lines ,
77+ new SettingColor (0 , 255 , 200 ),
78+ new SettingColor (0 , 255 , 200 , 25 ),
79+ true ,
80+ new SettingColor (0 , 255 , 200 , 125 )
81+ )
82+ )
83+ .build ()
7084 );
7185
72- private final Setting <Boolean > tracers = sgGeneral .add (new BoolSetting .Builder ()
73- .name ("tracers" )
74- .description ("Render tracer lines." )
75- .defaultValue (false )
76- .build ()
86+ private final Setting <Map <Block , ESPBlockData >> blockConfigs =
87+ sgGeneral .add (
88+ new BlockDataSetting .Builder <ESPBlockData >()
89+ .name ("block-configs" )
90+ .description ("Config for each block." )
91+ .defaultData (defaultBlockConfig )
92+ .onChanged (configs -> {
93+ if (isActive () && Utils .canUpdate ()) onActivate ();
94+ })
95+ .build ()
96+ );
97+
98+ private final Setting <Boolean > tracers = sgGeneral .add (
99+ new BoolSetting .Builder ()
100+ .name ("tracers" )
101+ .description ("Render tracer lines." )
102+ .defaultValue (false )
103+ .build ()
77104 );
78105
79106 private final BlockPos .Mutable blockPos = new BlockPos .Mutable ();
80107
81- private final Long2ObjectMap <ESPChunk > chunks = new Long2ObjectOpenHashMap <>();
108+ private final Map <
109+ Block ,
110+ Map <Property <?>, Comparable <?>>
111+ > activeFilterCache = new HashMap <>();
112+
113+ private final Long2ObjectMap <ESPChunk > chunks =
114+ new Long2ObjectOpenHashMap <>();
82115 private final Set <ESPGroup > groups = new ReferenceOpenHashSet <>();
83- private final ExecutorService workerThread = Executors .newSingleThreadExecutor ();
116+ private final ExecutorService workerThread =
117+ Executors .newSingleThreadExecutor ();
84118
85119 private DimensionType lastDimension ;
86120
87121 public BlockESP () {
88- super (Categories .Render , "block-esp" , "Renders specified blocks through walls." , "search" );
89-
122+ super (
123+ Categories .Render ,
124+ "block-esp" ,
125+ "Renders specified blocks through walls." ,
126+ "search"
127+ );
90128 RainbowColors .register (this ::onTickRainbow );
91129 }
92130
@@ -161,8 +199,7 @@ private void onChunkData(ChunkDataEvent event) {
161199 private void searchChunk (Chunk chunk ) {
162200 workerThread .submit (() -> {
163201 if (!isActive ()) return ;
164- ESPChunk schunk = ESPChunk .searchChunk (chunk , blocks .get ());
165-
202+ ESPChunk schunk = ESPChunk .searchChunk (chunk , this );
166203 if (schunk .size () > 0 ) {
167204 synchronized (chunks ) {
168205 chunks .put (chunk .getPos ().toLong (), schunk );
@@ -189,8 +226,8 @@ private void onBlockUpdate(BlockUpdateEvent event) {
189226 int chunkZ = bz >> 4 ;
190227 long key = ChunkPos .toLong (chunkX , chunkZ );
191228
192- boolean added = blocks . get (). contains ( event .newState . getBlock ()) && !blocks . get (). contains ( event .oldState . getBlock () );
193- boolean removed = !added && !blocks . get (). contains ( event .newState . getBlock ()) && blocks . get (). contains ( event .oldState . getBlock () );
229+ boolean added = shouldRender ( event .newState ) && !shouldRender ( event .oldState );
230+ boolean removed = !added && !shouldRender ( event .newState ) && shouldRender ( event .oldState );
194231
195232 if (added || removed ) {
196233 workerThread .submit (() -> {
@@ -263,4 +300,56 @@ private void onRender(Render3DEvent event) {
263300 public String getInfoString () {
264301 return "%s groups" .formatted (groups .size ());
265302 }
303+
304+
305+ public boolean shouldRender (BlockState state ) {
306+ Block block = state .getBlock ();
307+
308+ if (blocks .get ().contains (block )) {
309+ ESPBlockData blockData = blockConfigs .get ().get (block );
310+ if (blockData != null && !blockData .stateFilters .isEmpty ()) {
311+ return matchesStateFilters (state , block , blockData .stateFilters );
312+ }
313+ return true ;
314+ }
315+
316+ // Check global state filters
317+ if (activeFilterCache .containsKey (block )) {
318+ Map <Property <?>, Comparable <?>> requiredProps = activeFilterCache .get (block );
319+ for (Map .Entry <Property <?>,Comparable <?>> entry : requiredProps .entrySet ()) {
320+ if (!state .get (entry .getKey ()).equals (entry .getValue ())) {
321+ return false ;
322+ }
323+ }
324+ return true ;
325+ }
326+ return false ;
327+ }
328+
329+ private boolean matchesStateFilters (BlockState state , Block block , List <String > filters ) {
330+ for (String filter : filters ) {
331+ try {
332+ // Parse "key=value" format
333+ String [] kv = filter .split ("=" );
334+ if (kv .length != 2 ) continue ;
335+
336+ String propertyName = kv [0 ].trim ();
337+ String expectedValue = kv [1 ].trim ();
338+
339+ Property <?> property = block .getStateManager ().getProperty (propertyName );
340+ if (property == null ) continue ;
341+
342+ Optional <?> parsedValue = property .parse (expectedValue );
343+ if (parsedValue .isEmpty ()) continue ;
344+
345+ if (!state .get (property ).equals (parsedValue .get ())) {
346+ return false ;
347+ }
348+ } catch (Exception e ) {
349+ // Invalid filter format
350+ }
351+ }
352+
353+ return true ;
354+ }
266355}
0 commit comments