Skip to content

Commit 3389b43

Browse files
committed
Breadcrumbs for other players
1 parent 1771626 commit 3389b43

File tree

5 files changed

+181
-16
lines changed

5 files changed

+181
-16
lines changed

README.md

270 Bytes

Breadcrumbs

  • Leaves a line trail behind you (toggle-able/pause-able).
  • Trail can be infinitely long
  • Settings: color, max sections, section length, thickness.
  • Trail can be applied to other players (toggleable unique colors for each player)
  • Settings: color, max sections, section length, thickness, targets, keep trails toggle, random colors toggle.

BreadCrumbs

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

Lines changed: 168 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
import java.util.ArrayDeque;
1212
import java.util.ArrayList;
1313
import java.util.Deque;
14+
import java.util.HashMap;
1415
import java.util.List;
16+
import java.util.Map;
17+
import java.util.UUID;
1518

1619
import net.minecraft.client.util.math.MatrixStack;
1720
import net.minecraft.util.math.MathHelper;
@@ -23,6 +26,7 @@
2326
import net.wurstclient.hack.Hack;
2427
import net.wurstclient.settings.ColorSetting;
2528
import net.wurstclient.settings.CheckboxSetting;
29+
import net.wurstclient.settings.EnumSetting;
2630
import net.wurstclient.settings.SliderSetting;
2731
import net.wurstclient.util.RenderUtils;
2832

@@ -34,6 +38,23 @@ public final class BreadcrumbsHack extends Hack
3438

3539
private final ColorSetting color =
3640
new ColorSetting("Color", "Trail color.", new Color(255, 64, 64));
41+
42+
private enum Target
43+
{
44+
YOU,
45+
OTHERS,
46+
BOTH
47+
}
48+
49+
private final EnumSetting<Target> target =
50+
new EnumSetting<>("Target", Target.values(), Target.YOU);
51+
private final ColorSetting otherColor = new ColorSetting("Other color",
52+
"Color for other players' trails.", new Color(64, 160, 255));
53+
private final CheckboxSetting keepOthersOnLeave =
54+
new CheckboxSetting("Keep other trails when out of view", false);
55+
// If enabled, assign bright random-ish colors to other players.
56+
private final CheckboxSetting randomBrightColors =
57+
new CheckboxSetting("Random bright colors for others", false);
3758
private final SliderSetting maxSections =
3859
new SliderSetting("Max sections", 1000, 100, MAX_SECTIONS_INFINITE, 50,
3960
net.wurstclient.settings.SliderSetting.ValueDisplay.INTEGER
@@ -47,12 +68,22 @@ public final class BreadcrumbsHack extends Hack
4768
private final CheckboxSetting paused = new CheckboxSetting("Paused", false);
4869

4970
private final Deque<Vec3d> points = new ArrayDeque<>();
71+
// map from player uuid to their breadcrumb points
72+
private final Map<UUID, Deque<Vec3d>> otherPoints = new HashMap<>();
73+
// previous target selection to detect changes
74+
private Target prevTarget = null;
75+
// per-player assigned colors when randomBrightColors is enabled
76+
private final Map<UUID, Color> playerColors = new HashMap<>();
5077

5178
public BreadcrumbsHack()
5279
{
5380
super("Breadcrumbs");
5481
setCategory(Category.RENDER);
5582
addSetting(color);
83+
addSetting(target);
84+
addSetting(otherColor);
85+
addSetting(randomBrightColors);
86+
addSetting(keepOthersOnLeave);
5687
addSetting(maxSections);
5788
addSetting(sectionLen);
5889
addSetting(lineThickness);
@@ -69,6 +100,8 @@ public String getRenderName()
69100
protected void onEnable()
70101
{
71102
points.clear();
103+
otherPoints.clear();
104+
prevTarget = target.getSelected();
72105
EVENTS.add(UpdateListener.class, this);
73106
EVENTS.add(RenderListener.class, this);
74107
}
@@ -79,13 +112,32 @@ protected void onDisable()
79112
EVENTS.remove(UpdateListener.class, this);
80113
EVENTS.remove(RenderListener.class, this);
81114
points.clear();
115+
otherPoints.clear();
82116
}
83117

84118
@Override
85119
public void onUpdate()
86120
{
87121
if(MC.player == null)
88122
return;
123+
// handle target changes: clear trails that no longer apply
124+
Target sel = target.getSelected();
125+
if(prevTarget == null)
126+
prevTarget = sel;
127+
if(sel != prevTarget)
128+
{
129+
// if we switched to only YOU, clear other players' trails
130+
if(sel == Target.YOU)
131+
otherPoints.clear();
132+
// if we switched to only OTHERS, clear your own trail
133+
if(sel == Target.OTHERS)
134+
points.clear();
135+
// if we switched to BOTH, clear otherPoints to avoid resurrecting
136+
// old trails
137+
if(sel == Target.BOTH)
138+
otherPoints.clear();
139+
prevTarget = sel;
140+
}
89141
// Do not add new points while paused
90142
if(paused.isChecked())
91143
return;
@@ -105,18 +157,128 @@ public void onUpdate()
105157
while(!infinite && points.size() > limit)
106158
points.pollFirst();
107159
}
160+
161+
// Track other players if enabled
162+
if(sel == Target.OTHERS || sel == Target.BOTH)
163+
{
164+
for(var p : MC.world.getPlayers())
165+
{
166+
if(p == MC.player)
167+
continue;
168+
UUID id = p.getUuid();
169+
// assign a color if needed
170+
if(randomBrightColors.isChecked()
171+
&& !playerColors.containsKey(id))
172+
{
173+
playerColors.put(id,
174+
generateBrightColor(playerColors.size()));
175+
}
176+
Deque<Vec3d> dq =
177+
otherPoints.computeIfAbsent(id, k -> new ArrayDeque<>());
178+
Vec3d pos = new Vec3d(p.getX(), p.getY(), p.getZ());
179+
if(dq.isEmpty())
180+
{
181+
dq.add(pos);
182+
continue;
183+
}
184+
Vec3d lastp = dq.peekLast();
185+
if(movedEnough(lastp, pos, sectionLen.getValue()))
186+
{
187+
dq.add(pos);
188+
int limit = maxSections.getValueI();
189+
boolean infinite = limit >= MAX_SECTIONS_INFINITE;
190+
while(!infinite && dq.size() > limit)
191+
dq.pollFirst();
192+
}
193+
}
194+
// Remove trails for players that left, unless user chose to keep
195+
if(!keepOthersOnLeave.isChecked())
196+
{
197+
otherPoints.keySet().removeIf(uuid -> {
198+
boolean gone = MC.world.getPlayerByUuid(uuid) == null;
199+
if(gone)
200+
playerColors.remove(uuid);
201+
return gone;
202+
});
203+
}
204+
}
108205
}
109206

110207
@Override
111208
public void onRender(MatrixStack matrixStack, float partialTicks)
112209
{
113-
if(points.size() < 2)
114-
return;
115-
List<Vec3d> list = new ArrayList<>(points);
116-
int c = RenderUtils.toIntColor(new float[]{color.getColorF()[0],
117-
color.getColorF()[1], color.getColorF()[2]}, 0.8F);
118210
double thickness = lineThickness.getValue();
119-
RenderUtils.drawCurvedLine(matrixStack, list, c, false, thickness);
211+
Target sel = target.getSelected();
212+
// render your trail only when YOU or BOTH selected
213+
if((sel == Target.YOU || sel == Target.BOTH) && points.size() >= 2)
214+
{
215+
List<Vec3d> list = new ArrayList<>(points);
216+
int c = RenderUtils.toIntColor(new float[]{color.getColorF()[0],
217+
color.getColorF()[1], color.getColorF()[2]}, 0.8F);
218+
RenderUtils.drawCurvedLine(matrixStack, list, c, false, thickness);
219+
}
220+
221+
// render other players' trails
222+
if(sel == Target.OTHERS || sel == Target.BOTH)
223+
{
224+
for(var entry : otherPoints.entrySet())
225+
{
226+
Deque<Vec3d> dq = entry.getValue();
227+
if(dq.size() < 2)
228+
continue;
229+
UUID id = entry.getKey();
230+
int oc;
231+
if(randomBrightColors.isChecked())
232+
{
233+
Color col =
234+
playerColors.getOrDefault(id, otherColor.getColor());
235+
oc = RenderUtils
236+
.toIntColor(
237+
new float[]{col.getRed() / 255f,
238+
col.getGreen() / 255f, col.getBlue() / 255f},
239+
0.9F);
240+
}else
241+
{
242+
oc = RenderUtils.toIntColor(new float[]{
243+
otherColor.getColorF()[0], otherColor.getColorF()[1],
244+
otherColor.getColorF()[2]}, 0.8F);
245+
}
246+
List<Vec3d> l = new ArrayList<>(dq);
247+
RenderUtils.drawCurvedLine(matrixStack, l, oc, false,
248+
thickness);
249+
}
250+
}
251+
}
252+
253+
/**
254+
* Generate a bright color. We start from a palette of bright base colors
255+
* and if there are more players than base colors we generate darker
256+
* or lighter shades by cycling through a brightness multiplier.
257+
*/
258+
private Color generateBrightColor(int index)
259+
{
260+
Color[] base = new Color[]{new Color(255, 64, 64), // red
261+
new Color(64, 255, 64), // green
262+
new Color(64, 64, 255), // blue
263+
new Color(255, 196, 64), // orange
264+
new Color(196, 64, 255), // magenta
265+
new Color(64, 255, 196), // aqua
266+
new Color(255, 64, 196), // pink
267+
new Color(196, 255, 64), // lime
268+
new Color(64, 196, 255) // sky
269+
};
270+
int baseCount = base.length;
271+
int b = index % baseCount;
272+
int round = index / baseCount;
273+
float factor = 1.0f - Math.min(0.5f, round * 0.15f); // reduce
274+
// brightness
275+
// slightly per
276+
// round
277+
Color bc = base[b];
278+
int r = Math.min(255, Math.max(0, (int)(bc.getRed() * factor)));
279+
int g = Math.min(255, Math.max(0, (int)(bc.getGreen() * factor)));
280+
int bl = Math.min(255, Math.max(0, (int)(bc.getBlue() * factor)));
281+
return new Color(r, g, bl);
120282
}
121283

122284
private boolean movedEnough(Vec3d a, Vec3d b, double min)

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,13 @@ public class ChestEspHack extends Hack implements UpdateListener,
132132

133133
private final List<ChestEspEntityGroup> entityGroups =
134134
Arrays.asList(chestCarts, chestBoats, hopperCarts);
135-
135+
136136
// New: optionally show detected count in HackList
137137
private final CheckboxSetting showCountInHackList = new CheckboxSetting(
138138
"HackList count",
139139
"Appends the number of detected chests/containers to this hack's entry in the HackList.",
140140
false);
141-
141+
142142
private int foundCount;
143143

144144
public ChestEspHack()
@@ -213,7 +213,7 @@ else if(entity instanceof HopperMinecartEntity)
213213
else if(entity instanceof ChestBoatEntity
214214
|| entity instanceof ChestRaftEntity)
215215
chestBoats.add(entity);
216-
216+
217217
// compute found count from enabled groups (clamped)
218218
int total = groups.stream().mapToInt(g -> g.getBoxes().size()).sum();
219219
total += entityGroups.stream().mapToInt(g -> g.getBoxes().size()).sum();
@@ -273,7 +273,7 @@ private void renderTracers(MatrixStack matrixStack, float partialTicks)
273273
false);
274274
}
275275
}
276-
276+
277277
@Override
278278
public String getRenderName()
279279
{

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,13 @@ public final class MobEspHack extends Hack implements UpdateListener,
8080
FilterArmorStandsSetting.genericVision(true));
8181

8282
private final ArrayList<LivingEntity> mobs = new ArrayList<>();
83-
83+
8484
// New: optionally show detected count in HackList
8585
private final CheckboxSetting showCountInHackList = new CheckboxSetting(
8686
"HackList count",
8787
"Appends the number of detected mobs to this hack's entry in the HackList.",
8888
false);
89-
89+
9090
private int foundCount;
9191

9292
public MobEspHack()
@@ -136,7 +136,7 @@ public void onUpdate()
136136
// update count for HUD (clamped to 999)
137137
foundCount = Math.min(mobs.size(), 999);
138138
}
139-
139+
140140
@Override
141141
public String getRenderName()
142142
{

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,12 @@ else if(bh != null)
167167
return;
168168

169169
// Cancel the normal interaction and interact with the block behind
170-
// the frame/sign instead.
170+
// the frame/sign instead. Use InteractionSimulator to mirror vanilla
171+
// behavior and avoid accidentally starting item use (e.g. eating a
172+
// carrot) after opening the container.
171173
event.cancel();
172-
IMC.getInteractionManager().rightClickBlock(bhit.getBlockPos(),
173-
bhit.getSide(), bhit.getPos());
174+
// match vanilla: set item use cooldown before simulating
175+
MC.itemUseCooldown = 4;
176+
net.wurstclient.util.InteractionSimulator.rightClickBlock(bhit);
174177
}
175178
}

0 commit comments

Comments
 (0)