Skip to content

Commit 5cc8625

Browse files
committed
Shader-Safe Mode
1 parent 13321d0 commit 5cc8625

File tree

7 files changed

+434
-16
lines changed

7 files changed

+434
-16
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,14 @@ Examples:
663663
- ChunkSearcher now snapshots each chunk's block-state palettes on the client thread and lets the async scan read from that immutable copy, preventing the off-thread palette races that causes rare crashes.
664664
- Fixed rare empty outline shape crash (safe bounding box for empty shapes fix)
665665

666+
### Shader-Safe Mode
667+
- Detect shader usage (Iris/OptiFine) safely at runtime and keep a cached toggle state.
668+
- Automatically switch sensitive hacks to “shader-safe mode” when shaders are active.
669+
- Avoid render-thread crashes by snapshotting collections, disabling parallel streams, and avoiding unsafe joins.
670+
- Apply safe-mode behavior dynamically when shaders are toggled on/off.
671+
- Notify users once per toggle/enable so they know performance/behavior is adjusted.
672+
- Currently only in these higher risk hacks: Search, CaveFinder, PlayerESP and Excavator as well as in BlockVertexCompiler.
673+
666674
### Notes
667675
- Scanning only includes server-loaded chunks. Larger radii work best in single-player or on high view distance servers.
668676

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

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import net.wurstclient.util.RegionPos;
3838
import net.wurstclient.util.RenderUtils;
3939
import net.wurstclient.util.RotationUtils;
40+
import net.wurstclient.util.ShaderUtils; // ### ADDED ###
4041
import net.wurstclient.util.chunk.ChunkSearcher;
4142
import net.wurstclient.util.chunk.ChunkSearcherCoordinator;
4243

@@ -70,6 +71,9 @@ public final class CaveFinderHack extends Hack
7071
private ForkJoinPool forkJoinPool;
7172
private ForkJoinTask<HashSet<BlockPos>> getMatchingBlocksTask;
7273
private ForkJoinTask<ArrayList<int[]>> compileVerticesTask;
74+
private boolean shaderSafeMode; // ### ADDED ###
75+
private int buildGeneration; // ### ADDED ###
76+
private int currentBuildGeneration; // ### ADDED ###
7377

7478
private EasyVertexBuffer vertexBuffer;
7579
private RegionPos bufferRegion;
@@ -92,9 +96,17 @@ protected void onEnable()
9296
notify = true;
9397

9498
forkJoinPool = new ForkJoinPool();
99+
shaderSafeMode = ShaderUtils.refreshShadersActive(); // ### ADDED ###
100+
buildGeneration = 0; // ### ADDED ###
101+
currentBuildGeneration = 0; // ### ADDED ###
95102

96103
bufferUpToDate = false;
97-
104+
if(shaderSafeMode) // ### ADDED ###
105+
ChatUtils
106+
.message("Shaders detected - using safe mode for CaveFinder."); // ###
107+
// ADDED
108+
// ###
109+
98110
EVENTS.add(UpdateListener.class, this);
99111
EVENTS.add(PacketInputListener.class, coordinator);
100112
EVENTS.add(RenderListener.class, this);
@@ -120,6 +132,25 @@ protected void onDisable()
120132
@Override
121133
public void onUpdate()
122134
{
135+
boolean currentShaderSafeMode = ShaderUtils.refreshShadersActive(); // ###
136+
// ADDED
137+
// ###
138+
if(currentShaderSafeMode != shaderSafeMode) // ### ADDED ###
139+
{
140+
shaderSafeMode = currentShaderSafeMode; // ### MODIFIED ###
141+
stopBuildingBuffer(); // ### MODIFIED ###
142+
if(shaderSafeMode) // ### ADDED ###
143+
ChatUtils.message(
144+
"Shaders detected - using safe mode for CaveFinder."); // ###
145+
// ADDED
146+
// ###
147+
else
148+
ChatUtils.message(
149+
"Shaders disabled - returning CaveFinder to normal mode."); // ###
150+
// ADDED
151+
// ###
152+
}
153+
123154
boolean searchersChanged = coordinator.update();
124155

125156
if(searchersChanged)
@@ -136,6 +167,12 @@ public void onUpdate()
136167
notify = true;
137168
}
138169

170+
if(shaderSafeMode) // ### ADDED ###
171+
{
172+
buildBufferSafeMode(); // ### ADDED ###
173+
return; // ### ADDED ###
174+
}
175+
139176
// build the buffer
140177

141178
if(getMatchingBlocksTask == null)
@@ -176,6 +213,7 @@ public void onRender(PoseStack matrixStack, float partialTicks)
176213

177214
private void stopBuildingBuffer()
178215
{
216+
buildGeneration++; // ### ADDED ###
179217
if(getMatchingBlocksTask != null)
180218
{
181219
getMatchingBlocksTask.cancel(true);
@@ -196,6 +234,7 @@ private void startGetMatchingBlocksTask()
196234
BlockPos eyesPos = BlockPos.containing(RotationUtils.getEyesPos());
197235
Comparator<BlockPos> comparator =
198236
Comparator.comparingInt(pos -> eyesPos.distManhattan(pos));
237+
currentBuildGeneration = buildGeneration; // ### ADDED ###
199238

200239
getMatchingBlocksTask = forkJoinPool.submit(() -> coordinator
201240
.getMatches().parallel().map(ChunkSearcher.Result::pos)
@@ -205,6 +244,12 @@ private void startGetMatchingBlocksTask()
205244

206245
private void startCompileVerticesTask()
207246
{
247+
if(currentBuildGeneration != buildGeneration) // ### ADDED ###
248+
{
249+
stopBuildingBuffer(); // ### ADDED ###
250+
return; // ### ADDED ###
251+
}
252+
208253
HashSet<BlockPos> matchingBlocks = getMatchingBlocksTask.join();
209254

210255
if(matchingBlocks.size() < limit.getValueLog())
@@ -221,9 +266,56 @@ else if(notify)
221266
.submit(() -> BlockVertexCompiler.compile(matchingBlocks));
222267
}
223268

269+
private void buildBufferSafeMode() // ### ADDED ###
270+
{
271+
if(bufferUpToDate) // ### ADDED ###
272+
return; // ### ADDED ###
273+
274+
if(getMatchingBlocksTask != null || compileVerticesTask != null) // ###
275+
// ADDED
276+
// ###
277+
stopBuildingBuffer(); // ### ADDED ###
278+
279+
BlockPos eyesPos = BlockPos.containing(RotationUtils.getEyesPos());
280+
Comparator<BlockPos> comparator =
281+
Comparator.comparingInt(pos -> eyesPos.distManhattan(pos));
282+
java.util.ArrayList<ChunkSearcher.Result> matches =
283+
coordinator.getMatches().collect(
284+
java.util.stream.Collectors.toCollection(ArrayList::new));
285+
HashSet<BlockPos> matchingBlocks =
286+
matches.stream().map(ChunkSearcher.Result::pos).sorted(comparator)
287+
.limit(limit.getValueLog())
288+
.collect(Collectors.toCollection(HashSet::new));
289+
290+
if(matchingBlocks.size() < limit.getValueLog())
291+
notify = true;
292+
else if(notify)
293+
{
294+
ChatUtils.warning("CaveFinder found \u00a7lA LOT\u00a7r of blocks!"
295+
+ " To prevent lag, it will only show the closest \u00a76"
296+
+ limit.getValueString() + "\u00a7r results.");
297+
notify = false;
298+
}
299+
300+
ArrayList<int[]> vertices = BlockVertexCompiler.compile(matchingBlocks);
301+
setBufferFromVertices(vertices);
302+
}
303+
224304
private void setBufferFromTask()
225305
{
306+
if(currentBuildGeneration != buildGeneration) // ### ADDED ###
307+
{
308+
stopBuildingBuffer(); // ### ADDED ###
309+
return; // ### ADDED ###
310+
}
311+
226312
ArrayList<int[]> vertices = compileVerticesTask.join();
313+
setBufferFromVertices(vertices); // ### MODIFIED ###
314+
}
315+
316+
private void setBufferFromVertices(ArrayList<int[]> vertices) // ### ADDED
317+
// ###
318+
{
227319
RegionPos region = RenderUtils.getCameraRegion();
228320

229321
if(vertexBuffer != null)

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

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,11 @@
3838
import net.wurstclient.settings.SliderSetting.ValueDisplay;
3939
import net.wurstclient.util.BlockBreaker;
4040
import net.wurstclient.util.BlockUtils;
41+
import net.wurstclient.util.ChatUtils; // ### ADDED ###
4142
import net.wurstclient.util.OverlayRenderer;
4243
import net.wurstclient.util.RenderUtils;
4344
import net.wurstclient.util.RotationUtils;
45+
import net.wurstclient.util.ShaderUtils; // ### ADDED ###
4446

4547
public final class ExcavatorHack extends Hack
4648
implements UpdateListener, RenderListener, GUIRenderListener
@@ -52,6 +54,7 @@ public final class ExcavatorHack extends Hack
5254
new EnumSetting<>("Mode", Mode.values(), Mode.FAST);
5355

5456
private final OverlayRenderer overlay = new OverlayRenderer();
57+
private boolean shaderSafeMode; // ### ADDED ###
5558

5659
private Step step;
5760
private BlockPos posLookingAt;
@@ -99,7 +102,13 @@ protected void onEnable()
99102
WURST.getHax().veinMinerHack.setEnabled(false);
100103

101104
step = Step.START_POS;
102-
105+
shaderSafeMode = ShaderUtils.refreshShadersActive(); // ### ADDED ###
106+
if(shaderSafeMode) // ### ADDED ###
107+
ChatUtils
108+
.message("Shaders detected - using safe mode for Excavator."); // ###
109+
// ADDED
110+
// ###
111+
103112
EVENTS.add(UpdateListener.class, this);
104113
EVENTS.add(RenderListener.class, this);
105114
EVENTS.add(GUIRenderListener.class, this);
@@ -129,6 +138,24 @@ protected void onDisable()
129138
@Override
130139
public void onUpdate()
131140
{
141+
boolean currentShaderSafeMode = ShaderUtils.refreshShadersActive(); // ###
142+
// ADDED
143+
// ###
144+
if(currentShaderSafeMode != shaderSafeMode) // ### ADDED ###
145+
{
146+
shaderSafeMode = currentShaderSafeMode; // ### MODIFIED ###
147+
if(shaderSafeMode) // ### ADDED ###
148+
ChatUtils.message(
149+
"Shaders detected - using safe mode for Excavator."); // ###
150+
// ADDED
151+
// ###
152+
else
153+
ChatUtils.message(
154+
"Shaders disabled - returning Excavator to normal mode."); // ###
155+
// ADDED
156+
// ###
157+
}
158+
132159
if(step.selectPos)
133160
handlePositionSelection();
134161
else if(step == Step.SCAN_AREA)
@@ -374,8 +401,16 @@ Comparator.<BlockPos> comparingInt(pos -> pos.getY()).reversed()
374401
Predicate<BlockPos> pBreakable = MC.player.getAbilities().instabuild
375402
? BlockUtils::canBeClicked : pos -> BlockUtils.canBeClicked(pos)
376403
&& !BlockUtils.isUnbreakable(pos);
377-
area.remainingBlocks =
378-
(int)area.blocksList.parallelStream().filter(pBreakable).count();
404+
java.util.List<BlockPos> blocksSnapshot =
405+
shaderSafeMode ? new ArrayList<>(area.blocksList) : null; // ###
406+
// ADDED
407+
// ###
408+
if(shaderSafeMode) // ### ADDED ###
409+
area.remainingBlocks =
410+
(int)blocksSnapshot.stream().filter(pBreakable).count();
411+
else
412+
area.remainingBlocks = (int)area.blocksList.parallelStream()
413+
.filter(pBreakable).count();
379414

380415
if(area.remainingBlocks == 0)
381416
{
@@ -385,8 +420,12 @@ Comparator.<BlockPos> comparingInt(pos -> pos.getY()).reversed()
385420

386421
if(pathFinder == null)
387422
{
388-
BlockPos closestBlock = area.blocksList.parallelStream()
389-
.filter(pBreakable).min(cNextTargetBlock).get();
423+
java.util.stream.Stream<BlockPos> blockStream = shaderSafeMode
424+
? blocksSnapshot.stream() : area.blocksList.parallelStream(); // ###
425+
// MODIFIED
426+
// ###
427+
BlockPos closestBlock =
428+
blockStream.filter(pBreakable).min(cNextTargetBlock).get();
390429

391430
pathFinder = new ExcavatorPathFinder(closestBlock);
392431
}

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

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import net.wurstclient.util.RenderUtils;
5252
import net.wurstclient.util.RenderUtils.ColoredBox;
5353
import net.wurstclient.util.RenderUtils.ColoredPoint;
54+
import net.wurstclient.util.ShaderUtils; // ### ADDED ###
5455

5556
@SearchTags({"player esp", "PlayerTracers", "player tracers"})
5657
public final class PlayerEspHack extends Hack implements UpdateListener,
@@ -68,6 +69,7 @@ public final class PlayerEspHack extends Hack implements UpdateListener,
6869
new FilterInvisibleSetting("Won't show invisible players.", false));
6970

7071
private final ArrayList<Player> players = new ArrayList<>();
72+
private boolean shaderSafeMode; // ### ADDED ###
7173
private final Map<UUID, PendingEnterAlert> pendingEnterAlerts =
7274
new HashMap<>();
7375
// Alert settings & tracking for enter/exit notifications
@@ -203,6 +205,12 @@ protected void onEnable()
203205
EVENTS.add(CameraTransformViewBobbingListener.class, this);
204206
EVENTS.add(RenderListener.class, this);
205207
alertManager.addListener(alertListener);
208+
shaderSafeMode = ShaderUtils.refreshShadersActive(); // ### ADDED ###
209+
if(shaderSafeMode) // ### ADDED ###
210+
ChatUtils
211+
.message("Shaders detected - using safe mode for PlayerESP."); // ###
212+
// ADDED
213+
// ###
206214
}
207215

208216
@Override
@@ -219,13 +227,38 @@ protected void onDisable()
219227
@Override
220228
public void onUpdate()
221229
{
230+
boolean currentShaderSafeMode = ShaderUtils.refreshShadersActive(); // ###
231+
// ADDED
232+
// ###
233+
if(currentShaderSafeMode != shaderSafeMode) // ### ADDED ###
234+
{
235+
shaderSafeMode = currentShaderSafeMode; // ### MODIFIED ###
236+
if(shaderSafeMode) // ### ADDED ###
237+
ChatUtils.message(
238+
"Shaders detected - using safe mode for PlayerESP."); // ###
239+
// ADDED
240+
// ###
241+
else
242+
ChatUtils.message(
243+
"Shaders disabled - returning PlayerESP to normal mode."); // ###
244+
// ADDED
245+
// ###
246+
}
247+
222248
players.clear();
223249

224-
Stream<AbstractClientPlayer> stream = MC.level.players()
225-
.parallelStream().filter(e -> !e.isRemoved() && e.getHealth() > 0)
226-
.filter(e -> e != MC.player)
227-
.filter(e -> !(e instanceof FakePlayerEntity))
228-
.filter(e -> Math.abs(e.getY() - MC.player.getY()) <= 1e6);
250+
java.util.List<AbstractClientPlayer> playerSnapshot =
251+
shaderSafeMode ? new ArrayList<>(MC.level.players()) : null; // ###
252+
// ADDED
253+
// ###
254+
Stream<AbstractClientPlayer> stream = (shaderSafeMode
255+
? playerSnapshot.stream() : MC.level.players().parallelStream()) // ###
256+
// MODIFIED
257+
// ###
258+
.filter(e -> !e.isRemoved() && e.getHealth() > 0)
259+
.filter(e -> e != MC.player)
260+
.filter(e -> !(e instanceof FakePlayerEntity))
261+
.filter(e -> Math.abs(e.getY() - MC.player.getY()) <= 1e6);
229262

230263
// If enabled, filter out players that aren't present on the client's
231264
// player list (likely NPCs spawned by server plugins).

0 commit comments

Comments
 (0)