Skip to content

Commit 0f341b0

Browse files
committed
Added LavaWaterESP, Improved Search
1 parent 8c79efd commit 0f341b0

File tree

7 files changed

+376
-12
lines changed

7 files changed

+376
-12
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ All credit for the original client goes to Wurst-Imperium and its contributors.
5656
- Colors: fixed, rainbow, or by owner type.
5757
- Toggles for held-trident highlights.
5858

59+
### LavaWaterESP
60+
- Highlights lava and water blocks (source and flowing) with boxes and or traces.
61+
- Per type toggles, color and transparency sliders.
62+
- Render cap and adjustable chunk radius to keep FPS down and the view unobstructive.
63+
5964
### Waypoints
6065
- Create and save waypoints, with optional automatic death waypoints for all players.
6166
- Manager UI (`.waypoints` or apostrophe key).
@@ -103,13 +108,17 @@ Examples:
103108
- Multi-term queries: comma-separated (e.g., `ore, ancient`).
104109
- Rendering: Boxes, Lines, or Both. Tracers cancel view-bobbing.
105110
- Fixed/rainbow line colors.
111+
- New minimum search to 100 results.
112+
- Replaced full-sort approach to bounded max-heap (PriorityQueue) that keeps the closest N matches. This avoids sorting entire result and keeps search as fast as X-Ray.
106113
- Safer rescans and better crash handling.
107114

108115
### X-Ray improvements
109116
- Added ESP (Highlight Corners and or Fill Blocks)
110117
- Uses cached positions for speed
118+
- Optional transparency slider for ESP
111119
- Multi-term queries: comma-separated (e.g., `diamond, ancient`).
112120
- Opacity, block type changes and 'only show exposed' apply live without toggling.
121+
- New minimum search to 100 blocks.
113122

114123
### Nuker improvements
115124
- Auto toggle AutoTool option (If it wasn't on already, it will be enabled when using Nuker then turned off with Nuker)

src/main/java/net/wurstclient/hack/HackList.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ public final class HackList implements UpdateListener
152152
public final NukerHack nukerHack = new NukerHack();
153153
public final NukerLegitHack nukerLegitHack = new NukerLegitHack();
154154
public final OpenWaterEspHack openWaterEspHack = new OpenWaterEspHack();
155+
public final LavaWaterEspHack lavaWaterEspHack = new LavaWaterEspHack();
155156
public final OverlayHack overlayHack = new OverlayHack();
156157
public final PanicHack panicHack = new PanicHack();
157158
public final ParkourHack parkourHack = new ParkourHack();
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
/*
2+
* Copyright (c) 2014-2025 Wurst-Imperium and contributors.
3+
*
4+
* This source code is subject to the terms of the GNU General Public
5+
* License, version 3. If a copy of the GPL was not distributed with this
6+
* file, You can obtain one at: https://www.gnu.org/licenses/gpl-3.0.txt
7+
*/
8+
package net.wurstclient.hacks;
9+
10+
import java.awt.Color;
11+
import java.util.Arrays;
12+
import java.util.Comparator;
13+
import java.util.List;
14+
import java.util.function.BiPredicate;
15+
import java.util.stream.Collectors;
16+
17+
import net.minecraft.block.Block;
18+
import net.minecraft.block.BlockState;
19+
import net.minecraft.block.Blocks;
20+
import net.minecraft.client.util.math.MatrixStack;
21+
import net.minecraft.util.math.BlockPos;
22+
import net.minecraft.util.math.Box;
23+
import net.minecraft.util.math.ChunkPos;
24+
import net.minecraft.util.math.Vec3d;
25+
import net.wurstclient.Category;
26+
import net.wurstclient.SearchTags;
27+
import net.wurstclient.events.CameraTransformViewBobbingListener;
28+
import net.wurstclient.events.RenderListener;
29+
import net.wurstclient.events.UpdateListener;
30+
import net.wurstclient.hack.Hack;
31+
import net.wurstclient.hacks.portalesp.LiquidEspBlockGroup;
32+
import net.wurstclient.settings.CheckboxSetting;
33+
import net.wurstclient.settings.ChunkAreaSetting;
34+
import net.wurstclient.settings.ColorSetting;
35+
import net.wurstclient.settings.EspStyleSetting;
36+
import net.wurstclient.settings.SliderSetting;
37+
import net.wurstclient.settings.SliderSetting.ValueDisplay;
38+
import net.wurstclient.util.RotationUtils;
39+
import net.wurstclient.util.RenderUtils;
40+
import net.wurstclient.util.chunk.ChunkSearcher.Result;
41+
import net.wurstclient.util.chunk.ChunkSearcherCoordinator;
42+
43+
@SearchTags({"lavaesp", "wateresp", "lava water", "LavaWaterESP"})
44+
public final class LavaWaterEspHack extends Hack implements UpdateListener,
45+
CameraTransformViewBobbingListener, RenderListener
46+
{
47+
private final EspStyleSetting style = new EspStyleSetting();
48+
private final CheckboxSetting stickyArea =
49+
new CheckboxSetting("Sticky area",
50+
"Off: Re-centers every chunk to match ESP drop-off.\n"
51+
+ "On: Keeps results anchored so you can path back to them.",
52+
false);
53+
private final ChunkAreaSetting area = new ChunkAreaSetting("Area",
54+
"The area around the player to search in.\n"
55+
+ "Higher values require a faster computer.",
56+
ChunkAreaSetting.ChunkArea.A3);
57+
58+
// Default colors: lava orange, water blue
59+
private final LiquidEspBlockGroup lavaGroup =
60+
new LiquidEspBlockGroup(Blocks.LAVA,
61+
new ColorSetting("Lava color",
62+
"Lava will be highlighted in this color.", new Color(0xFF8C00)),
63+
new CheckboxSetting("Include lava", true));
64+
private final LiquidEspBlockGroup waterGroup = new LiquidEspBlockGroup(
65+
Blocks.WATER,
66+
new ColorSetting("Water color",
67+
"Water will be highlighted in this color.", new Color(0x3F76E4)),
68+
new CheckboxSetting("Include water", true));
69+
private final List<LiquidEspBlockGroup> groups =
70+
Arrays.asList(lavaGroup, waterGroup);
71+
72+
// Transparency sliders per type (0-255)
73+
private final SliderSetting lavaAlpha =
74+
new SliderSetting("Lava transparency",
75+
"Transparency for lava (0 = fully transparent, 255 = opaque).", 64,
76+
0, 255, 1, ValueDisplay.INTEGER);
77+
private final SliderSetting waterAlpha =
78+
new SliderSetting("Water transparency",
79+
"Transparency for water (0 = fully transparent, 255 = opaque).", 64,
80+
0, 255, 1, ValueDisplay.INTEGER);
81+
82+
// How many blocks to render (100 - 1000)
83+
private final SliderSetting renderAmount = new SliderSetting(
84+
"Render amount", "Maximum number of blocks to render at once.", 100,
85+
100, 1000, 10, ValueDisplay.INTEGER);
86+
87+
private final BiPredicate<BlockPos, BlockState> query =
88+
(pos, state) -> isTargetBlock(state.getBlock());
89+
private final ChunkSearcherCoordinator coordinator =
90+
new ChunkSearcherCoordinator(query, area);
91+
private boolean groupsUpToDate;
92+
private ChunkAreaSetting.ChunkArea lastAreaSelection;
93+
private ChunkPos lastPlayerChunk;
94+
95+
public LavaWaterEspHack()
96+
{
97+
super("LavaWaterESP");
98+
setCategory(Category.RENDER);
99+
addSetting(style);
100+
groups.stream().flatMap(LiquidEspBlockGroup::getSettings)
101+
.forEach(this::addSetting);
102+
addSetting(area);
103+
addSetting(stickyArea);
104+
// add transparency and render amount settings
105+
addSetting(lavaAlpha);
106+
addSetting(waterAlpha);
107+
addSetting(renderAmount);
108+
}
109+
110+
private boolean isTargetBlock(Block b)
111+
{
112+
for(LiquidEspBlockGroup g : groups)
113+
if(g.getBlock() == b)
114+
return true;
115+
return false;
116+
}
117+
118+
@Override
119+
protected void onEnable()
120+
{
121+
groupsUpToDate = false;
122+
lastAreaSelection = area.getSelected();
123+
lastPlayerChunk = new ChunkPos(MC.player.getBlockPos());
124+
EVENTS.add(UpdateListener.class, this);
125+
EVENTS.add(CameraTransformViewBobbingListener.class, this);
126+
EVENTS.add(RenderListener.class, this);
127+
EVENTS.add(net.wurstclient.events.PacketInputListener.class,
128+
coordinator);
129+
}
130+
131+
@Override
132+
protected void onDisable()
133+
{
134+
EVENTS.remove(UpdateListener.class, this);
135+
EVENTS.remove(CameraTransformViewBobbingListener.class, this);
136+
EVENTS.remove(RenderListener.class, this);
137+
EVENTS.remove(net.wurstclient.events.PacketInputListener.class,
138+
coordinator);
139+
coordinator.reset();
140+
groups.forEach(LiquidEspBlockGroup::clear);
141+
}
142+
143+
@Override
144+
public void onUpdate()
145+
{
146+
ChunkAreaSetting.ChunkArea currentArea = area.getSelected();
147+
if(currentArea != lastAreaSelection)
148+
{
149+
lastAreaSelection = currentArea;
150+
coordinator.reset();
151+
groupsUpToDate = false;
152+
}
153+
// Recenter per chunk when sticky is off
154+
ChunkPos currentChunk = new ChunkPos(MC.player.getBlockPos());
155+
if(!stickyArea.isChecked() && !currentChunk.equals(lastPlayerChunk))
156+
{
157+
lastPlayerChunk = currentChunk;
158+
coordinator.reset();
159+
groupsUpToDate = false;
160+
}
161+
boolean searchersChanged = coordinator.update();
162+
if(searchersChanged)
163+
groupsUpToDate = false;
164+
if(!groupsUpToDate && coordinator.isDone())
165+
updateGroupBoxes();
166+
}
167+
168+
@Override
169+
public void onCameraTransformViewBobbing(
170+
CameraTransformViewBobbingEvent event)
171+
{
172+
if(style.getSelected().hasLines())
173+
event.cancel();
174+
}
175+
176+
@Override
177+
public void onRender(MatrixStack matrixStack, float partialTicks)
178+
{
179+
if(style.getSelected().hasBoxes())
180+
renderBoxes(matrixStack);
181+
if(style.getSelected().hasLines())
182+
renderTracers(matrixStack, partialTicks);
183+
}
184+
185+
private void renderBoxes(MatrixStack matrixStack)
186+
{
187+
for(LiquidEspBlockGroup group : groups)
188+
{
189+
if(!group.isEnabled())
190+
continue;
191+
List<Box> boxes = group.getBoxes();
192+
int alpha = group == lavaGroup ? lavaAlpha.getValueI()
193+
: waterAlpha.getValueI();
194+
int quadsColor = group.getColorI(alpha);
195+
int linesColor = group.getColorI(alpha);
196+
RenderUtils.drawSolidBoxes(matrixStack, boxes, quadsColor, false);
197+
RenderUtils.drawOutlinedBoxes(matrixStack, boxes, linesColor,
198+
false);
199+
}
200+
}
201+
202+
private void renderTracers(MatrixStack matrixStack, float partialTicks)
203+
{
204+
for(LiquidEspBlockGroup group : groups)
205+
{
206+
if(!group.isEnabled())
207+
continue;
208+
List<Box> boxes = group.getBoxes();
209+
List<Vec3d> ends = boxes.stream().map(Box::getCenter).toList();
210+
int alpha = group == lavaGroup ? lavaAlpha.getValueI()
211+
: waterAlpha.getValueI();
212+
int color = group.getColorI(alpha);
213+
RenderUtils.drawTracers(matrixStack, partialTicks, ends, color,
214+
false);
215+
}
216+
}
217+
218+
private void updateGroupBoxes()
219+
{
220+
groups.forEach(LiquidEspBlockGroup::clear);
221+
int limit = renderAmount.getValueI();
222+
java.util.List<Result> matches = coordinator.getMatches()
223+
.sorted(Comparator.comparingDouble(
224+
r -> r.pos().getSquaredDistance(RotationUtils.getEyesPos())))
225+
.limit(limit).collect(Collectors.toList());
226+
matches.forEach(this::addToGroupBoxes);
227+
groupsUpToDate = true;
228+
}
229+
230+
private void addToGroupBoxes(Result result)
231+
{
232+
for(LiquidEspBlockGroup group : groups)
233+
if(result.state().getBlock() == group.getBlock())
234+
{
235+
group.add(result.pos());
236+
break;
237+
}
238+
}
239+
}

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

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@
99

1010
import java.awt.Color;
1111
import java.util.ArrayList;
12-
import java.util.Comparator;
1312
import java.util.HashSet;
1413
import java.util.Locale;
1514
import java.util.Arrays;
15+
import java.util.PriorityQueue;
1616
import java.util.concurrent.ForkJoinPool;
1717
import java.util.concurrent.ForkJoinTask;
18-
import java.util.stream.Collectors;
1918

2019
import com.mojang.blaze3d.vertex.VertexFormat.DrawMode;
2120

@@ -95,7 +94,7 @@ private enum SearchMode
9594
private final SliderSetting limit = new SliderSetting("Limit",
9695
"The maximum number of blocks to display.\n"
9796
+ "Higher values require a faster computer.",
98-
4, 3, 6, 1, ValueDisplay.LOGARITHMIC);
97+
4, 2, 6, 1, ValueDisplay.LOGARITHMIC);
9998
private int prevLimit;
10099
private boolean notify;
101100

@@ -435,14 +434,32 @@ private boolean containsNormalized(String haystack, String normalizedQuery)
435434

436435
private void startGetMatchingBlocksTask()
437436
{
437+
// Use a bounded max-heap to keep only the closest N matches without
438+
// sorting the entire result set. This avoids long pauses when scanning
439+
// very large areas.
438440
BlockPos eyesPos = BlockPos.ofFloored(RotationUtils.getEyesPos());
439-
Comparator<BlockPos> comparator =
440-
Comparator.comparingInt(pos -> eyesPos.getManhattanDistance(pos));
441-
442-
getMatchingBlocksTask = forkJoinPool.submit(() -> coordinator
443-
.getMatches().parallel().map(ChunkSearcher.Result::pos)
444-
.sorted(comparator).limit(limit.getValueLog())
445-
.collect(Collectors.toCollection(HashSet::new)));
441+
final int limitCount = limit.getValueLog();
442+
getMatchingBlocksTask = forkJoinPool.submit(() -> {
443+
PriorityQueue<BlockPos> heap = new PriorityQueue<>((limitCount + 1),
444+
(a, b) -> Integer.compare(b.getManhattanDistance(eyesPos),
445+
a.getManhattanDistance(eyesPos)));
446+
java.util.Iterator<ChunkSearcher.Result> it =
447+
coordinator.getMatches().iterator();
448+
while(it.hasNext())
449+
{
450+
ChunkSearcher.Result r = it.next();
451+
BlockPos pos = r.pos();
452+
if(heap.size() < limitCount)
453+
heap.offer(pos);
454+
else if(pos.getManhattanDistance(eyesPos) < heap.peek()
455+
.getManhattanDistance(eyesPos))
456+
{
457+
heap.poll();
458+
heap.offer(pos);
459+
}
460+
}
461+
return new HashSet<>(heap);
462+
});
446463
}
447464

448465
private void startCompileVerticesTask()

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import net.wurstclient.settings.TextFieldSetting;
4242
import net.wurstclient.settings.CheckboxSetting;
4343
import net.wurstclient.settings.ChunkAreaSetting;
44+
import net.wurstclient.settings.SliderSetting;
4445
import net.wurstclient.util.BlockUtils;
4546
import net.wurstclient.util.ChatUtils;
4647
import net.wurstclient.util.chunk.ChunkSearcherCoordinator;
@@ -139,6 +140,10 @@ private static enum Mode
139140
private final SliderSetting highlightAlpha =
140141
new SliderSetting("Highlight transparency", 80, 1, 100, 1,
141142
ValueDisplay.INTEGER.withSuffix("%"));
143+
// Maximum number of highlighted blocks to render (log scale)
144+
private final SliderSetting renderAmount = new SliderSetting(
145+
"Render amount", "The maximum number of blocks to render.", 3, 2, 6, 1,
146+
ValueDisplay.LOGARITHMIC);
142147
// (block transparency override removed)
143148
private final ChunkAreaSetting area = new ChunkAreaSetting("Area",
144149
"The area around the player to search in. Higher values require a faster computer.");
@@ -168,6 +173,7 @@ public XRayHack()
168173
addSetting(highlightFill);
169174
addSetting(highlightColor);
170175
addSetting(highlightAlpha);
176+
addSetting(renderAmount);
171177
optiFineWarning = checkOptiFine();
172178
// Coordinator query: lightweight matching (ID + simple name), exposure
173179
// filtering is applied on main thread when building boxes
@@ -401,8 +407,13 @@ public void onRender(MatrixStack matrices, float partialTicks)
401407
if(!highlightPositionsUpToDate && coordinator.isDone())
402408
{
403409
highlightPositions.clear();
404-
coordinator.getMatches()
405-
.forEach(r -> highlightPositions.add(r.pos()));
410+
// collect nearest N positions (limit controlled by renderAmount)
411+
BlockPos playerPos = MC.player.getBlockPos();
412+
java.util.Comparator<BlockPos> comparator = java.util.Comparator
413+
.comparingInt(p -> playerPos.getManhattanDistance(p));
414+
coordinator.getMatches().map(r -> r.pos()).sorted(comparator)
415+
.limit(renderAmount.getValueLog())
416+
.forEach(highlightPositions::add);
406417
highlightPositionsUpToDate = true;
407418
visibleBoxesUpToDate = false;
408419
}

0 commit comments

Comments
 (0)