Skip to content

Commit 5433212

Browse files
committed
Update ClientChatOverlay. Fix OppStats, RoofESP, Multiplayer Mixin
1 parent b786b77 commit 5433212

7 files changed

Lines changed: 361 additions & 52 deletions

File tree

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import net.minecraft.client.gui.Font;
1212
import net.minecraft.client.gui.GuiGraphicsExtractor;
1313
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
14+
import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen;
15+
import net.minecraft.client.gui.screens.inventory.InventoryScreen;
1416
import java.io.IOException;
1517
import java.nio.file.Path;
1618
import java.util.HashMap;
@@ -321,10 +323,19 @@ private void buildNormally()
321323
{
322324
if(MC.screen instanceof AbstractContainerScreen)
323325
{
324-
ChatUtils
325-
.error("AutoBuild disabled: container opened during build.");
326-
setEnabled(false);
327-
return;
326+
if(MC.screen instanceof InventoryScreen
327+
|| MC.screen instanceof CreativeModeInventoryScreen)
328+
{
329+
// Allow opening player inventory while building so blocks can
330+
// be
331+
// moved/restocked without cancelling AutoBuild.
332+
}else
333+
{
334+
ChatUtils.error(
335+
"AutoBuild disabled: container opened during build.");
336+
setEnabled(false);
337+
return;
338+
}
328339
}
329340

330341
int beforeSize = remainingBlocks.size();

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ public final class ClientChatOverlayHack extends Hack
5959
new SliderSetting("Chat font size",
6060
"description.wurst.setting.clientchatoverlay.chat_font_scale", 1,
6161
0.5, 2, 0.05, ValueDisplay.DECIMAL.withSuffix("x"));
62+
private final ColorSetting defaultTextColor = new ColorSetting(
63+
"Default text color",
64+
"Base text color for overlay chat lines without explicit color styling.",
65+
new Color(0xC0C0C0));
6266
private final SliderSetting hudOffsetX = new SliderSetting("HUD X offset",
6367
0, -320, 320, 1, ValueDisplay.INTEGER);
6468
private final SliderSetting hudOffsetY = new SliderSetting("HUD Y offset",
@@ -80,6 +84,7 @@ public ClientChatOverlayHack()
8084
addSetting(ownUsernameColor);
8185
addSetting(maxLines);
8286
addSetting(chatFontScale);
87+
addSetting(defaultTextColor);
8388
addSetting(hudOffsetX);
8489
addSetting(hudOffsetY);
8590
}
@@ -149,6 +154,11 @@ public SliderSetting getChatFontScaleSetting()
149154
return chatFontScale;
150155
}
151156

157+
public int getDefaultTextColorI()
158+
{
159+
return defaultTextColor.getColorI() & 0x00FFFFFF;
160+
}
161+
152162
public void resetVanillaChatScale()
153163
{
154164
if(MC == null || MC.options == null)

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ public void onUpdate()
165165
{
166166
if(p == MC.player)
167167
continue;
168+
if(!onlineNow.contains(p.getUUID()))
169+
continue;
168170
if(ignoreNpcs.isChecked()
169171
&& isBotLikeEntity(p.getUUID(), p.getName().getString()))
170172
continue;
@@ -182,7 +184,6 @@ && isBotLikeEntity(p.getUUID(), p.getName().getString()))
182184
private void updateFromLivePlayer(OppRecord rec, Player p, long now)
183185
{
184186
rec.name = p.getName().getString();
185-
rec.online = true;
186187
rec.lastPos = p.position();
187188
rec.lastSeenAt = now;
188189
rec.distance = MC.player == null ? Double.NaN : p.distanceTo(MC.player);
@@ -364,6 +365,11 @@ private void loadCurrentServerData()
364365
OppRecord rec = OppRecord.fromJson(el.getAsJsonObject());
365366
records.put(rec.uuid, rec);
366367
}
368+
369+
// Saved online state is stale across sessions.
370+
// Current tab list should be the only source of truth.
371+
for(OppRecord rec : records.values())
372+
rec.online = false;
367373
}catch(Exception e)
368374
{
369375
ChatUtils.error("OppStats load failed: " + e.getMessage());

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

Lines changed: 125 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313
import java.util.ArrayList;
1414
import java.util.Comparator;
1515
import java.util.HashSet;
16+
import java.util.List;
1617
import java.util.UUID;
1718
import net.minecraft.ChatFormatting;
1819
import net.minecraft.core.BlockPos;
1920
import net.minecraft.network.chat.Component;
21+
import net.minecraft.sounds.SoundEvents;
2022
import net.minecraft.world.entity.Entity;
2123
import net.minecraft.world.entity.item.ItemEntity;
2224
import net.minecraft.world.item.ItemStack;
@@ -25,6 +27,7 @@
2527
import net.minecraft.world.level.block.Block;
2628
import net.minecraft.world.level.block.Blocks;
2729
import net.minecraft.world.level.chunk.LevelChunk;
30+
import net.minecraft.world.level.chunk.LevelChunkSection;
2831
import net.minecraft.world.phys.AABB;
2932
import net.minecraft.world.phys.Vec3;
3033
import net.wurstclient.Category;
@@ -60,6 +63,9 @@ public final class RoofEspHack extends Hack implements UpdateListener,
6063
new CheckboxSetting("Chat alerts",
6164
"Sends a chat alert when the number of RoofESP detections changes.",
6265
false);
66+
private final CheckboxSetting soundAlerts = new CheckboxSetting(
67+
"Sound alerts",
68+
"Plays a sound when the number of RoofESP detections changes.", false);
6369
private final SliderSetting alertCooldown =
6470
new SliderSetting("Alert cooldown", "Minimum time between chat alerts.",
6571
5, 0, 60, 1, SliderSetting.ValueDisplay.INTEGER.withSuffix("s"));
@@ -86,6 +92,13 @@ public final class RoofEspHack extends Hack implements UpdateListener,
8692
"Block render limit",
8793
"Maximum amount of roof blocks to process each scan. 0 = unlimited.",
8894
500, 0, 5000, 1, SliderSetting.ValueDisplay.INTEGER);
95+
private final CheckboxSetting detectLowerLadders =
96+
new CheckboxSetting("Detect ladders 118-128",
97+
"Also detect ladder blocks between Y=118 and Y=127 in the Nether.",
98+
false);
99+
private final CheckboxSetting ignoreNaturalRoofBlocks =
100+
new CheckboxSetting("Ignore natural roof blocks",
101+
"Hides natural roof noise blocks (red/brown mushrooms).", true);
89102

90103
private final ArrayList<AABB> boxes = new ArrayList<>();
91104
private final ArrayList<Vec3> tracerEnds = new ArrayList<>();
@@ -109,13 +122,16 @@ public RoofEspHack()
109122
addSetting(highlightFill);
110123
addSetting(tracerFlash);
111124
addSetting(chatAlerts);
125+
addSetting(soundAlerts);
112126
addSetting(alertCooldown);
113127
addSetting(ignoreOwnDrops);
114128
addSetting(showCountInHackList);
115129
addSetting(stickyArea);
116130
addSetting(area);
117131
addSetting(blockScanInterval);
118132
addSetting(blockRenderLimit);
133+
addSetting(detectLowerLadders);
134+
addSetting(ignoreNaturalRoofBlocks);
119135
}
120136

121137
@Override
@@ -172,7 +188,7 @@ public void onUpdate()
172188
targets.addAll(cachedBlockTargets);
173189
collectRoofItems(targets);
174190

175-
targets.sort(Comparator.comparingDouble(t -> t.distanceSq));
191+
targets.sort(Comparator.comparingDouble(this::distanceSqToPlayer));
176192
int limit = getEffectiveGlobalEspLimit();
177193
if(limit > 0 && targets.size() > limit)
178194
targets.subList(limit, targets.size()).clear();
@@ -202,44 +218,84 @@ private void updateBlockCache()
202218

203219
private void collectRoofBlocks(ArrayList<Target> targets)
204220
{
205-
int minY = Math.max(ROOF_Y, MC.level.getMinY());
221+
if(MC.level == null)
222+
return;
223+
224+
int worldMinY = MC.level.getMinY();
225+
int minY = Math.max(ROOF_Y, worldMinY);
226+
int ladderMinY = Math.max(ROOF_Y - 10, MC.level.getMinY());
227+
boolean includeLowerLadders = detectLowerLadders.isChecked();
206228
int maxYExclusive = MC.level.getMaxY();
207-
if(minY >= maxYExclusive)
229+
if(minY >= maxYExclusive && !includeLowerLadders)
208230
return;
209231
int cap = blockRenderLimit.getValueI();
210232
HashSet<Long> dedupe = new HashSet<>();
211233

212-
for(LevelChunk chunk : getChunksInScanArea())
234+
for(LevelChunk chunk : getChunksInScanAreaSortedByDistance())
213235
{
214236
if(cap > 0 && targets.size() >= cap)
215237
break;
216238

239+
LevelChunkSection[] sections = chunk.getSections();
240+
if(sections == null || sections.length == 0)
241+
continue;
242+
217243
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
218-
int xStart = chunk.getPos().getMinBlockX();
219-
int zStart = chunk.getPos().getMinBlockZ();
220-
for(int y = minY; y < maxYExclusive; y++)
244+
int scanMinY = includeLowerLadders ? ladderMinY : minY;
245+
int firstSectionY = chunk.getMinY() >> 4;
246+
int minSectionIndex = Math.max(0, (scanMinY >> 4) - firstSectionY);
247+
int maxSectionIndex = Math.min(sections.length - 1,
248+
((maxYExclusive - 1) >> 4) - firstSectionY);
249+
for(int sectionIndex =
250+
minSectionIndex; sectionIndex <= maxSectionIndex; sectionIndex++)
221251
{
252+
LevelChunkSection section = sections[sectionIndex];
253+
if(section == null || section.hasOnlyAir())
254+
continue;
255+
222256
if(cap > 0 && targets.size() >= cap)
223257
break;
224-
for(int x = xStart; x < xStart + 16; x++)
258+
259+
int baseY = (firstSectionY + sectionIndex) << 4;
260+
for(int ly = 0; ly < 16; ly++)
225261
{
262+
int y = baseY + ly;
263+
if(y < scanMinY || y >= maxYExclusive)
264+
continue;
265+
226266
if(cap > 0 && targets.size() >= cap)
227267
break;
228-
for(int z = zStart; z < zStart + 16; z++)
268+
for(int lx = 0; lx < 16; lx++)
229269
{
230-
pos.set(x, y, z);
231-
Block block = chunk.getBlockState(pos).getBlock();
232-
if(block == Blocks.AIR || block == Blocks.CAVE_AIR
233-
|| block == Blocks.VOID_AIR)
234-
continue;
235-
if(block == Blocks.RED_MUSHROOM
236-
|| block == Blocks.BROWN_MUSHROOM)
237-
continue;
238-
long key = pos.asLong();
239-
if(!dedupe.add(key))
240-
continue;
241-
AABB box = new AABB(pos);
242-
targets.add(new Target(box, box.getCenter()));
270+
if(cap > 0 && targets.size() >= cap)
271+
break;
272+
int x = chunk.getPos().getMinBlockX() + lx;
273+
for(int lz = 0; lz < 16; lz++)
274+
{
275+
pos.set(x, y, chunk.getPos().getMinBlockZ() + lz);
276+
Block block =
277+
section.getBlockState(lx, ly, lz).getBlock();
278+
if(y < ROOF_Y)
279+
{
280+
if(!includeLowerLadders
281+
|| block != Blocks.LADDER)
282+
continue;
283+
}else
284+
{
285+
if(block == Blocks.AIR
286+
|| block == Blocks.CAVE_AIR
287+
|| block == Blocks.VOID_AIR)
288+
continue;
289+
if(ignoreNaturalRoofBlocks.isChecked()
290+
&& isNaturalRoofBlock(block))
291+
continue;
292+
}
293+
long key = pos.asLong();
294+
if(!dedupe.add(key))
295+
continue;
296+
AABB box = new AABB(pos);
297+
targets.add(new Target(box, box.getCenter()));
298+
}
243299
}
244300
}
245301
}
@@ -268,6 +324,26 @@ private Iterable<LevelChunk> getChunksInScanArea()
268324
return chunks;
269325
}
270326

327+
private List<LevelChunk> getChunksInScanAreaSortedByDistance()
328+
{
329+
ArrayList<LevelChunk> chunks = new ArrayList<>();
330+
for(LevelChunk chunk : getChunksInScanArea())
331+
chunks.add(chunk);
332+
if(anchorChunk == null)
333+
return chunks;
334+
chunks.sort(Comparator.comparingInt(chunk -> {
335+
int dx = chunk.getPos().x() - anchorChunk.x();
336+
int dz = chunk.getPos().z() - anchorChunk.z();
337+
return dx * dx + dz * dz;
338+
}));
339+
return chunks;
340+
}
341+
342+
private boolean isNaturalRoofBlock(Block block)
343+
{
344+
return block == Blocks.RED_MUSHROOM || block == Blocks.BROWN_MUSHROOM;
345+
}
346+
271347
private int getChunkRange(ChunkAreaSetting.ChunkArea selectedArea)
272348
{
273349
String enumName = selectedArea.name(); // A11 -> 11x11
@@ -388,29 +464,45 @@ private int getEffectiveGlobalEspLimit()
388464
.getEffectiveGlobalEspRenderLimit();
389465
}
390466

467+
private double distanceSqToPlayer(Target target)
468+
{
469+
return MC.player == null ? Double.MAX_VALUE
470+
: MC.player.distanceToSqr(target.end());
471+
}
472+
391473
private void sendAlertIfNeeded(int currentCount)
392474
{
393-
if(!chatAlerts.isChecked())
475+
boolean chatEnabled = chatAlerts.isChecked();
476+
boolean soundEnabled = soundAlerts.isChecked();
477+
if(!chatEnabled && !soundEnabled)
394478
{
395479
lastAlertCount = currentCount;
396480
return;
397481
}
482+
398483
if(currentCount == lastAlertCount)
399484
return;
485+
400486
long now = System.currentTimeMillis();
401487
long cooldownMs = alertCooldown.getValueI() * 1000L;
402488
if(now - lastAlertMs < cooldownMs)
403489
return;
490+
404491
lastAlertMs = now;
405492
lastAlertCount = currentCount;
406-
ChatUtils.component(Component.literal("[RoofESP] ")
407-
.withStyle(ChatFormatting.DARK_RED)
408-
.append(
409-
Component.literal("Detected ").withStyle(ChatFormatting.GRAY))
410-
.append(Component.literal(Integer.toString(currentCount))
411-
.withStyle(ChatFormatting.RED))
412-
.append(Component.literal(" roof targets at/above Y=128.")
413-
.withStyle(ChatFormatting.GRAY)));
493+
494+
if(chatEnabled)
495+
ChatUtils.component(Component.literal("[RoofESP] ")
496+
.withStyle(ChatFormatting.DARK_RED)
497+
.append(Component.literal("Detected ")
498+
.withStyle(ChatFormatting.GRAY))
499+
.append(Component.literal(Integer.toString(currentCount))
500+
.withStyle(ChatFormatting.RED))
501+
.append(Component.literal(" roof targets at/above Y=128.")
502+
.withStyle(ChatFormatting.GRAY)));
503+
504+
if(soundEnabled && MC.player != null)
505+
MC.player.playSound(SoundEvents.EXPERIENCE_ORB_PICKUP, 0.7F, 1.35F);
414506
}
415507

416508
@Override
@@ -445,12 +537,8 @@ public void onRender(PoseStack matrixStack, float partialTicks)
445537
}
446538
}
447539

448-
private record Target(AABB box, Vec3 end, double distanceSq)
540+
private record Target(AABB box, Vec3 end)
449541
{
450-
private Target(AABB box, Vec3 end)
451-
{
452-
this(box, end, MC.player == null ? Double.MAX_VALUE
453-
: MC.player.distanceToSqr(end));
454-
}
542+
// compact immutable render target
455543
}
456544
}

0 commit comments

Comments
 (0)