Skip to content

Commit bdf5775

Browse files
committed
Updated BedESP
1 parent 913cab4 commit bdf5775

File tree

2 files changed

+222
-1
lines changed

2 files changed

+222
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ Build without the flag to get the full CevAPI experience; build with the flag fo
4848
- Finds all bed types.
4949
- Chunk-based scanning with configurable color and Box/Line style.
5050
- Rendering: Boxes, Lines, or Both in a fixed color.
51+
- Filter for hiding Trial Chamber and or Villager beds.
5152

5253
![BedESP](https://i.imgur.com/kPHKPDz.png)
5354

src/main/java/net/wurstclient/hacks/BedEspHack.java

Lines changed: 221 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,25 @@
1111
import java.util.Arrays;
1212
import java.util.List;
1313
import java.util.function.BiPredicate;
14+
import java.util.stream.Collectors;
15+
import java.util.stream.StreamSupport;
1416

17+
import net.minecraft.block.Block;
1518
import net.minecraft.block.BlockState;
1619
import net.minecraft.block.BedBlock;
20+
import net.minecraft.block.Blocks;
21+
import net.minecraft.block.DoorBlock;
22+
import net.minecraft.block.entity.BlockEntity;
23+
import net.minecraft.block.entity.TrialSpawnerBlockEntity;
24+
import net.minecraft.block.enums.BedPart;
1725
import net.minecraft.client.util.math.MatrixStack;
26+
import net.minecraft.entity.Entity;
27+
import net.minecraft.entity.passive.IronGolemEntity;
28+
import net.minecraft.entity.passive.VillagerEntity;
29+
import net.minecraft.registry.Registries;
30+
import net.minecraft.registry.RegistryKeys;
31+
import net.minecraft.registry.tag.TagKey;
32+
import net.minecraft.util.Identifier;
1833
import net.minecraft.util.math.BlockPos;
1934
import net.minecraft.util.math.Box;
2035
import net.minecraft.util.math.ChunkPos;
@@ -33,8 +48,10 @@
3348
import net.wurstclient.settings.EspStyleSetting;
3449
import net.wurstclient.settings.SliderSetting;
3550
import net.wurstclient.util.RenderUtils;
51+
import net.wurstclient.util.BlockUtils;
3652
import net.wurstclient.util.chunk.ChunkSearcher.Result;
3753
import net.wurstclient.util.chunk.ChunkSearcherCoordinator;
54+
import net.wurstclient.util.chunk.ChunkUtils;
3855

3956
@SearchTags({"BedESP", "bed esp"})
4057
public final class BedEspHack extends Hack implements UpdateListener,
@@ -68,6 +85,12 @@ public final class BedEspHack extends Hack implements UpdateListener,
6885
"Only show beds at or above the configured Y level.", false);
6986
private final SliderSetting aboveGroundY = new SliderSetting(
7087
"Set ESP Y limit", 62, -65, 255, 1, SliderSetting.ValueDisplay.INTEGER);
88+
private final net.wurstclient.settings.CheckboxSetting filterTrialChambers =
89+
new net.wurstclient.settings.CheckboxSetting("Filter trial chambers",
90+
"Hides beds that match common trial chamber layouts.", false);
91+
private final net.wurstclient.settings.CheckboxSetting filterVillageBeds =
92+
new net.wurstclient.settings.CheckboxSetting("Filter village beds",
93+
"Hides beds that appear to belong to villages.", false);
7194

7295
private final BiPredicate<BlockPos, BlockState> query =
7396
(pos, state) -> state.getBlock() instanceof BedBlock;
@@ -78,6 +101,13 @@ public final class BedEspHack extends Hack implements UpdateListener,
78101
private boolean groupsUpToDate;
79102
private ChunkPos lastPlayerChunk;
80103
private int foundCount;
104+
private List<BlockPos> cachedTrialSpawners = List.of();
105+
private List<Vec3d> cachedVillagerPositions = List.of();
106+
private List<Vec3d> cachedGolemPositions = List.of();
107+
private static final TagKey<Block> WAXED_COPPER_BLOCKS_TAG = TagKey.of(
108+
RegistryKeys.BLOCK, Identifier.of("minecraft", "waxed_copper_blocks"));
109+
private boolean lastTrialFilterState;
110+
private boolean lastVillageFilterState;
81111

82112
public BedEspHack()
83113
{
@@ -91,6 +121,11 @@ public BedEspHack()
91121
addSetting(stickyArea);
92122
addSetting(onlyAboveGround);
93123
addSetting(aboveGroundY);
124+
addSetting(filterTrialChambers);
125+
addSetting(filterVillageBeds);
126+
127+
lastTrialFilterState = filterTrialChambers.isChecked();
128+
lastVillageFilterState = filterVillageBeds.isChecked();
94129
}
95130

96131
@Override
@@ -102,6 +137,8 @@ protected void onEnable()
102137
EVENTS.add(CameraTransformViewBobbingListener.class, this);
103138
EVENTS.add(RenderListener.class, this);
104139
lastPlayerChunk = new ChunkPos(MC.player.getBlockPos());
140+
lastTrialFilterState = filterTrialChambers.isChecked();
141+
lastVillageFilterState = filterVillageBeds.isChecked();
105142
}
106143

107144
@Override
@@ -116,6 +153,9 @@ protected void onDisable()
116153
groups.forEach(BedEspBlockGroup::clear);
117154
// reset count
118155
foundCount = 0;
156+
cachedTrialSpawners = List.of();
157+
cachedVillagerPositions = List.of();
158+
cachedGolemPositions = List.of();
119159
}
120160

121161
@Override
@@ -132,6 +172,10 @@ public void onUpdate()
132172
coordinator.reset();
133173
groupsUpToDate = false;
134174
}
175+
176+
if(didFiltersChange())
177+
groupsUpToDate = false;
178+
135179
if(!groupsUpToDate && coordinator.isDone())
136180
updateGroupBoxes();
137181
}
@@ -191,6 +235,7 @@ private void updateGroupBoxes()
191235
{
192236
groups.forEach(BedEspBlockGroup::clear);
193237
java.util.List<Result> results = coordinator.getMatches().toList();
238+
refreshEnvironmentalCaches();
194239
results.forEach(this::addToGroupBoxes);
195240
groupsUpToDate = true;
196241
// update count for HUD (clamped to 999) based on displayed boxes
@@ -209,13 +254,188 @@ public String getRenderName()
209254

210255
private void addToGroupBoxes(Result result)
211256
{
257+
BlockState state = result.state();
258+
if(!(state.getBlock() instanceof BedBlock)
259+
|| state.get(BedBlock.PART) == BedPart.FOOT)
260+
return;
261+
262+
BlockPos headPos = result.pos();
212263
if(onlyAboveGround.isChecked()
213-
&& result.pos().getY() < aboveGroundY.getValue())
264+
&& headPos.getY() < aboveGroundY.getValue())
265+
return;
266+
267+
if(filterTrialChambers.isChecked() && isTrialChamberBed(headPos))
268+
return;
269+
270+
if(filterVillageBeds.isChecked() && isLikelyVillageBed(headPos))
214271
return;
215272
for(BedEspBlockGroup group : groups)
216273
{
217274
group.add(result);
218275
break;
219276
}
220277
}
278+
279+
private void refreshEnvironmentalCaches()
280+
{
281+
if(filterTrialChambers.isChecked())
282+
cachedTrialSpawners = collectTrialSpawnerPositions();
283+
else
284+
cachedTrialSpawners = List.of();
285+
286+
if(filterVillageBeds.isChecked())
287+
{
288+
cachedVillagerPositions =
289+
collectEntityPositions(VillagerEntity.class);
290+
cachedGolemPositions =
291+
collectEntityPositions(IronGolemEntity.class);
292+
}else
293+
{
294+
cachedVillagerPositions = List.of();
295+
cachedGolemPositions = List.of();
296+
}
297+
}
298+
299+
private boolean didFiltersChange()
300+
{
301+
boolean trial = filterTrialChambers.isChecked();
302+
boolean village = filterVillageBeds.isChecked();
303+
if(trial != lastTrialFilterState || village != lastVillageFilterState)
304+
{
305+
lastTrialFilterState = trial;
306+
lastVillageFilterState = village;
307+
return true;
308+
}
309+
310+
return false;
311+
}
312+
313+
private List<BlockPos> collectTrialSpawnerPositions()
314+
{
315+
if(MC.world == null)
316+
return List.of();
317+
318+
return ChunkUtils.getLoadedBlockEntities()
319+
.filter(be -> be instanceof TrialSpawnerBlockEntity)
320+
.map(BlockEntity::getPos).map(BlockPos::toImmutable)
321+
.collect(Collectors.toList());
322+
}
323+
324+
private <T extends Entity> List<Vec3d> collectEntityPositions(Class<T> type)
325+
{
326+
if(MC.world == null)
327+
return List.of();
328+
329+
return StreamSupport.stream(MC.world.getEntities().spliterator(), false)
330+
.filter(e -> !e.isRemoved()).filter(type::isInstance)
331+
.map(entity -> Vec3d.ofCenter(entity.getBlockPos()))
332+
.collect(Collectors.toList());
333+
}
334+
335+
private boolean isTrialChamberBed(BlockPos headPos)
336+
{
337+
int y = headPos.getY();
338+
if(y < -38 || y > 10)
339+
return false;
340+
341+
if(!isNearWaxedCopper(headPos, 5))
342+
return false;
343+
344+
return isNearTrialSpawner(headPos, 100);
345+
}
346+
347+
private boolean isNearWaxedCopper(BlockPos center, int range)
348+
{
349+
if(MC.world == null)
350+
return false;
351+
352+
return BlockUtils.getAllInBoxStream(center, range)
353+
.anyMatch(pos -> isWaxedCopper(BlockUtils.getState(pos)));
354+
}
355+
356+
private boolean isWaxedCopper(BlockState state)
357+
{
358+
if(state.isIn(WAXED_COPPER_BLOCKS_TAG))
359+
return true;
360+
361+
String idPath = Registries.BLOCK.getId(state.getBlock()).getPath();
362+
return idPath.contains("waxed") && idPath.contains("copper");
363+
}
364+
365+
private boolean isNearTrialSpawner(BlockPos center, int range)
366+
{
367+
if(cachedTrialSpawners.isEmpty())
368+
return false;
369+
370+
double rangeSq = range * range;
371+
Vec3d centerVec = Vec3d.ofCenter(center);
372+
return cachedTrialSpawners.stream().anyMatch(
373+
pos -> Vec3d.ofCenter(pos).squaredDistanceTo(centerVec) <= rangeSq);
374+
}
375+
376+
private boolean isLikelyVillageBed(BlockPos headPos)
377+
{
378+
if(!hasDoorNearby(headPos, 4))
379+
return false;
380+
381+
boolean hasVillageEntity =
382+
isEntityWithinRange(cachedVillagerPositions, headPos, 24)
383+
|| isEntityWithinRange(cachedGolemPositions, headPos, 24);
384+
boolean hayCluster = hasHayBaleCluster(headPos, 6);
385+
386+
if(hasVillageEntity || hayCluster)
387+
return true;
388+
389+
return hasGlassPaneCluster(headPos, 4, 1);
390+
}
391+
392+
private boolean isEntityWithinRange(List<Vec3d> positions, BlockPos center,
393+
double range)
394+
{
395+
if(positions.isEmpty())
396+
return false;
397+
398+
double rangeSq = range * range;
399+
Vec3d centerVec = Vec3d.ofCenter(center);
400+
return positions.stream()
401+
.anyMatch(pos -> pos.squaredDistanceTo(centerVec) <= rangeSq);
402+
}
403+
404+
private boolean hasHayBaleCluster(BlockPos center, int range)
405+
{
406+
if(MC.world == null)
407+
return false;
408+
409+
long count = BlockUtils.getAllInBoxStream(center, range)
410+
.filter(pos -> BlockUtils.getBlock(pos) == Blocks.HAY_BLOCK)
411+
.limit(16).count();
412+
return count >= 4;
413+
}
414+
415+
private boolean hasDoorNearby(BlockPos center, int range)
416+
{
417+
if(MC.world == null)
418+
return false;
419+
420+
return BlockUtils.getAllInBoxStream(center, range)
421+
.anyMatch(pos -> BlockUtils.getBlock(pos) instanceof DoorBlock);
422+
}
423+
424+
private boolean hasGlassPaneCluster(BlockPos center, int range,
425+
int requiredCount)
426+
{
427+
if(MC.world == null)
428+
return false;
429+
430+
long glassCount = BlockUtils.getAllInBoxStream(center, range)
431+
.filter(pos -> isGlassPane(BlockUtils.getBlock(pos)))
432+
.limit(requiredCount).count();
433+
return glassCount >= requiredCount;
434+
}
435+
436+
private boolean isGlassPane(Block block)
437+
{
438+
String path = Registries.BLOCK.getId(block).getPath();
439+
return path.contains("glass_pane");
440+
}
221441
}

0 commit comments

Comments
 (0)