diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/BlockESP.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/BlockESP.java index 8cfe86c072..a4bf9f2755 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/BlockESP.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/BlockESP.java @@ -25,6 +25,8 @@ import net.minecraft.util.math.ChunkPos; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.dimension.DimensionType; +import net.minecraft.block.BlockState; +import net.minecraft.state.property.Property; import java.util.Iterator; import java.util.List; @@ -32,6 +34,8 @@ import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.HashMap; +import java.util.Optional; public class BlockESP extends Module { private final SettingGroup sgGeneral = settings.getDefaultGroup(); @@ -66,6 +70,9 @@ public class BlockESP extends Module { .name("block-configs") .description("Config for each block.") .defaultData(defaultBlockConfig) + .onChanged(configs -> { + if (isActive() && Utils.canUpdate()) onActivate(); + }) .build() ); @@ -78,6 +85,8 @@ public class BlockESP extends Module { private final BlockPos.Mutable blockPos = new BlockPos.Mutable(); + private final Map, Comparable>> activeFilterCache = new HashMap<>(); + private final Long2ObjectMap chunks = new Long2ObjectOpenHashMap<>(); private final Set groups = new ReferenceOpenHashSet<>(); private final ExecutorService workerThread = Executors.newSingleThreadExecutor(); @@ -161,8 +170,7 @@ private void onChunkData(ChunkDataEvent event) { private void searchChunk(Chunk chunk) { workerThread.submit(() -> { if (!isActive()) return; - ESPChunk schunk = ESPChunk.searchChunk(chunk, blocks.get()); - + ESPChunk schunk = ESPChunk.searchChunk(chunk, this); if (schunk.size() > 0) { synchronized (chunks) { chunks.put(chunk.getPos().toLong(), schunk); @@ -189,8 +197,8 @@ private void onBlockUpdate(BlockUpdateEvent event) { int chunkZ = bz >> 4; long key = ChunkPos.toLong(chunkX, chunkZ); - boolean added = blocks.get().contains(event.newState.getBlock()) && !blocks.get().contains(event.oldState.getBlock()); - boolean removed = !added && !blocks.get().contains(event.newState.getBlock()) && blocks.get().contains(event.oldState.getBlock()); + boolean added = shouldRender(event.newState) && !shouldRender(event.oldState); + boolean removed = !added && !shouldRender(event.newState) && shouldRender(event.oldState); if (added || removed) { workerThread.submit(() -> { @@ -263,4 +271,56 @@ private void onRender(Render3DEvent event) { public String getInfoString() { return "%s groups".formatted(groups.size()); } + + + public boolean shouldRender(BlockState state) { + Block block = state.getBlock(); + + if (blocks.get().contains(block)) { + ESPBlockData blockData = blockConfigs.get().get(block); + if (blockData != null && !blockData.stateFilters.isEmpty()) { + return matchesStateFilters(state, block, blockData.stateFilters); + } + return true; + } + + // Check global state filters + if (activeFilterCache.containsKey(block)) { + Map, Comparable> requiredProps = activeFilterCache.get(block); + for (Map.Entry,Comparable> entry : requiredProps.entrySet()) { + if (!state.get(entry.getKey()).equals(entry.getValue())) { + return false; + } + } + return true; + } + return false; + } + + private boolean matchesStateFilters(BlockState state, Block block, List filters) { + for (String filter : filters) { + try { + // Parse "key=value" format + String[] kv = filter.split("="); + if (kv.length != 2) continue; + + String propertyName = kv[0].trim(); + String expectedValue = kv[1].trim(); + + Property property = block.getStateManager().getProperty(propertyName); + if (property == null) continue; + + Optional parsedValue = property.parse(expectedValue); + if (parsedValue.isEmpty()) continue; + + if (!state.get(property).equals(parsedValue.get())) { + return false; + } + } catch (Exception e) { + // Invalid filter format + } + } + + return true; + } } diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/ESPBlock.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/ESPBlock.java index 809ea49ed5..fccd22a1de 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/ESPBlock.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/ESPBlock.java @@ -127,6 +127,8 @@ private boolean isNeighbour(Direction dir) { if (neighbourState.getBlock() != state.getBlock()) return false; + if (!blockEsp.shouldRender(neighbourState)) return false; + VoxelShape cube = VoxelShapes.fullCube(); VoxelShape shape = state.getOutlineShape(mc.world, blockPos); VoxelShape neighbourShape = neighbourState.getOutlineShape(mc.world, blockPos); @@ -165,7 +167,8 @@ private boolean isNeighbour(Direction dir) { private boolean isNeighbourDiagonal(double x, double y, double z) { blockPos.set(this.x + x, this.y + y, this.z + z); - return state.getBlock() == mc.world.getBlockState(blockPos).getBlock(); + BlockState neighbourState = mc.world.getBlockState(blockPos); + return state.getBlock() == neighbourState.getBlock() && blockEsp.shouldRender(neighbourState); } public void render(Render3DEvent event) { diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/ESPBlockData.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/ESPBlockData.java index 1979cd3922..a6dd4bef3d 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/ESPBlockData.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/ESPBlockData.java @@ -5,6 +5,10 @@ package meteordevelopment.meteorclient.systems.modules.render.blockesp; +import java.util.ArrayList; +import java.util.List; + + import meteordevelopment.meteorclient.gui.GuiTheme; import meteordevelopment.meteorclient.gui.WidgetScreen; import meteordevelopment.meteorclient.renderer.ShapeMode; @@ -16,6 +20,8 @@ import meteordevelopment.meteorclient.utils.render.color.SettingColor; import net.minecraft.block.Block; import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtList; +import net.minecraft.nbt.NbtString; public class ESPBlockData implements IGeneric, IChangeable, IBlockData { public ShapeMode shapeMode; @@ -25,6 +31,8 @@ public class ESPBlockData implements IGeneric, IChangeable, IBlock public boolean tracer; public SettingColor tracerColor; + public final List stateFilters = new ArrayList<>(); + private boolean changed; public ESPBlockData(ShapeMode shapeMode, SettingColor lineColor, SettingColor sideColor, boolean tracer, SettingColor tracerColor) { @@ -70,6 +78,9 @@ public ESPBlockData set(ESPBlockData value) { tracer = value.tracer; tracerColor.set(value.tracerColor); + stateFilters.clear(); + stateFilters.addAll(value.stateFilters); + changed = value.changed; return this; @@ -77,7 +88,9 @@ public ESPBlockData set(ESPBlockData value) { @Override public ESPBlockData copy() { - return new ESPBlockData(shapeMode, new SettingColor(lineColor), new SettingColor(sideColor), tracer, new SettingColor(tracerColor)); + ESPBlockData copy = new ESPBlockData(shapeMode, new SettingColor(lineColor), new SettingColor(sideColor), tracer, new SettingColor(tracerColor)); + copy.stateFilters.addAll(stateFilters); + return copy; } @Override @@ -91,6 +104,12 @@ public NbtCompound toTag() { tag.putBoolean("tracer", tracer); tag.put("tracerColor", tracerColor.toTag()); + NbtList filtersList = new NbtList(); + for (String filter : stateFilters) { + filtersList.add(NbtString.of(filter)); + } + tag.put("stateFilters", filtersList); + tag.putBoolean("changed", changed); return tag; @@ -105,6 +124,14 @@ public ESPBlockData fromTag(NbtCompound tag) { tracer = tag.getBoolean("tracer", false); tracerColor.fromTag(tag.getCompoundOrEmpty("tracerColor")); + stateFilters.clear(); + tag.getList("stateFilters").ifPresent(filtersList -> { + for (int i = 0; i < filtersList.size(); i++) { + filtersList.getString(i).ifPresent(stateFilters::add); + } + }); + + changed = tag.getBoolean("changed", false); return this; diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/ESPBlockDataScreen.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/ESPBlockDataScreen.java index 6854d2a89a..68271babf6 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/ESPBlockDataScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/ESPBlockDataScreen.java @@ -13,6 +13,8 @@ import net.minecraft.block.Block; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; + public class ESPBlockDataScreen extends WindowScreen { private final ESPBlockData blockData; private final Setting setting; @@ -39,6 +41,7 @@ public void initWidgets() { Settings settings = new Settings(); SettingGroup sgGeneral = settings.getDefaultGroup(); SettingGroup sgTracer = settings.createGroup("Tracer"); + SettingGroup sgFilters = settings.createGroup("stateFilters"); sgGeneral.add(new EnumSetting.Builder() .name("shape-mode") @@ -110,6 +113,20 @@ public void initWidgets() { .build() ); + // Add state filters setting + sgFilters.add(new StringListSetting.Builder() + .name("stateFilters") + .description("Filters with states (e.g. 'waterlogged=false', 'facing=north', 'ominous=true'). Only blocks matching ALL filters will be shown.") + .defaultValue(new ArrayList<>()) + .onModuleActivated(stringSetting -> stringSetting.set(blockData.stateFilters)) + .onChanged(filters -> { + blockData.stateFilters.clear(); + blockData.stateFilters.addAll(filters); + onChanged(); + }) + .build() + ); + settings.onActivated(); add(theme.settings(settings)).expandX(); } diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/ESPChunk.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/ESPChunk.java index 0fac693cf3..dd1ad74dd9 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/ESPChunk.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/render/blockesp/ESPChunk.java @@ -86,7 +86,7 @@ public void render(Render3DEvent event) { } - public static ESPChunk searchChunk(Chunk chunk, List blocks) { + public static ESPChunk searchChunk(Chunk chunk, BlockESP module) { ESPChunk schunk = new ESPChunk(chunk.getPos().x, chunk.getPos().z); if (schunk.shouldBeDeleted()) return schunk; @@ -100,7 +100,7 @@ public static ESPChunk searchChunk(Chunk chunk, List blocks) { blockPos.set(x, y, z); BlockState bs = chunk.getBlockState(blockPos); - if (blocks.contains(bs.getBlock())) schunk.add(blockPos, false); + if (module.shouldRender(bs)) schunk.add(blockPos, false); } } }