Skip to content

Commit c1acbcf

Browse files
committed
PacketRate Hack and Updated Wurst Options Mixin
1 parent 65c29bf commit c1acbcf

9 files changed

Lines changed: 362 additions & 15 deletions

File tree

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,12 @@ Reports number of waypoints changed.
487487
- Keybind works even with GUIs open (chests, portals, etc.).
488488
- Tick count displayed in HackList
489489

490+
### Packet Rate (Limiter)
491+
- Monitors outgoing packets per seconds and shows it in the hack list
492+
- Token-bucket limiter queues excess packets based on your chosen limit
493+
- Keep-alive packets bypass the limiter so server still sees you being there
494+
- Queue flushing runs every tick and sends packets in order while respecting your limit
495+
490496
## What's changed or improved in this fork?
491497

492498
### ChestESP
@@ -668,6 +674,12 @@ Examples:
668674

669675
### Alt Manager Improved
670676
- Can now multi-select and delete alt accounts
677+
- Moved to multiplayer screen
678+
679+
### Wurst Options
680+
- Added to home screen and put in the old spot of Alt Manager
681+
- Added reload settings button so you can modify the settings.json manually
682+
- Added open Wurst folder
671683

672684
### Unsafe Chat Toast
673685
- Optional; toggle via NoChatReports or Wurst Options.

src/main/java/net/wurstclient/WurstClient.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,11 @@ public void reloadSettings()
239239
settingsFile.load();
240240
}
241241

242+
public void reloadFromDisk()
243+
{
244+
reloadSettings();
245+
}
246+
242247
public ArrayList<Path> listSettingsProfiles()
243248
{
244249
if(!Files.isDirectory(settingsProfileFolder))

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

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,16 @@ public EnabledHacksFile(Path path)
3030
}
3131

3232
public void load(HackList hackList)
33+
{
34+
load(hackList, false);
35+
}
36+
37+
public void load(HackList hackList, boolean disableOnly)
3338
{
3439
try
3540
{
3641
WsonArray wson = JsonUtils.parseFileToArray(path);
37-
enableHacks(hackList, wson);
42+
enableHacks(hackList, wson, disableOnly);
3843

3944
}catch(NoSuchFileException e)
4045
{
@@ -56,17 +61,35 @@ public void loadProfile(HackList hax, Path profilePath)
5661
throw new IllegalArgumentException();
5762

5863
WsonArray wson = JsonUtils.parseFileToArray(profilePath);
59-
enableHacks(hax, wson);
64+
enableHacks(hax, wson, false);
6065

6166
save(hax);
6267
}
6368

64-
private void enableHacks(HackList hax, WsonArray wson)
69+
private void enableHacks(HackList hax, WsonArray wson, boolean disableOnly)
6570
{
6671
try
6772
{
6873
disableSaving = true;
6974

75+
if(disableOnly)
76+
{
77+
java.util.Set<String> desired = new java.util.HashSet<>();
78+
for(String name : wson.getAllStrings())
79+
desired.add(name);
80+
81+
for(Hack hack : hax.getAllHax())
82+
{
83+
if(!hack.isStateSaved())
84+
continue;
85+
86+
boolean shouldEnable = desired.contains(hack.getName());
87+
if(!shouldEnable && hack.isEnabled())
88+
hack.setEnabled(false);
89+
}
90+
return;
91+
}
92+
7093
for(Hack hack : hax.getAllHax())
7194
hack.setEnabled(false);
7295

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ public final class HackList implements UpdateListener
179179
public final OverlayHack overlayHack = new OverlayHack();
180180
public final PanicHack panicHack = new PanicHack();
181181
public final PacketDelayHack packetDelayHack = new PacketDelayHack();
182+
public final PacketRateHack packetRateHack = new PacketRateHack();
182183
public final ParkourHack parkourHack = new ParkourHack();
183184
public final PlayerEspHack playerEspHack = new PlayerEspHack();
184185
public final PortalEspHack portalEspHack = new PortalEspHack();
@@ -313,6 +314,11 @@ public void reloadEnabledHax()
313314
enabledHacksFile.load(this);
314315
}
315316

317+
public void reloadEnabledHaxDisableOnly()
318+
{
319+
enabledHacksFile.load(this, true);
320+
}
321+
316322
public void saveFavoriteHax()
317323
{
318324
favoriteHacksFile.save(this);
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
/*
2+
* Copyright (c) 2014-2026 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.util.ArrayDeque;
11+
12+
import net.minecraft.client.multiplayer.ClientPacketListener;
13+
import net.minecraft.network.protocol.Packet;
14+
15+
import net.wurstclient.Category;
16+
import net.wurstclient.SearchTags;
17+
import net.wurstclient.events.PacketOutputListener;
18+
import net.wurstclient.events.PacketOutputListener.PacketOutputEvent;
19+
import net.wurstclient.events.UpdateListener;
20+
import net.wurstclient.hack.Hack;
21+
import net.wurstclient.settings.CheckboxSetting;
22+
import net.wurstclient.settings.SliderSetting;
23+
import net.wurstclient.settings.SliderSetting.ValueDisplay;
24+
25+
@SearchTags({"PacketRate", "RateLimit", "packets per second", "pps"})
26+
public final class PacketRateHack extends Hack
27+
implements PacketOutputListener, UpdateListener
28+
{
29+
private final CheckboxSetting limiterEnabled =
30+
new CheckboxSetting("Enable limiter",
31+
"Turn off to only monitor packet rate without limiting it.", true);
32+
33+
private final SliderSetting limit = new SliderSetting("Limit",
34+
"Max outgoing packets per second.\n0 = unlimited", 100, 0, 1000, 1,
35+
ValueDisplay.INTEGER);
36+
37+
private final ArrayDeque<Packet<?>> queue = new ArrayDeque<>();
38+
private final ArrayDeque<Long> sentTimes = new ArrayDeque<>();
39+
private boolean flushing;
40+
private double tokens;
41+
private long lastRefillMs;
42+
43+
public PacketRateHack()
44+
{
45+
super("PacketRate");
46+
setCategory(Category.OTHER);
47+
addSetting(limiterEnabled);
48+
addSetting(limit);
49+
}
50+
51+
@Override
52+
public String getRenderName()
53+
{
54+
long now = System.currentTimeMillis();
55+
pruneSentTimes(now);
56+
int rate = sentTimes.size();
57+
58+
if(!limiterEnabled.isChecked())
59+
return getName() + " [" + rate + "/s]";
60+
61+
if(limit.getValueI() <= 0)
62+
return getName() + " [" + rate + "/s]";
63+
64+
return getName() + " [" + rate + "/s | lim " + limit.getValueI() + "]";
65+
}
66+
67+
@Override
68+
protected void onEnable()
69+
{
70+
queue.clear();
71+
sentTimes.clear();
72+
tokens = 0;
73+
lastRefillMs = System.currentTimeMillis();
74+
75+
EVENTS.add(PacketOutputListener.class, this);
76+
EVENTS.add(UpdateListener.class, this);
77+
}
78+
79+
@Override
80+
protected void onDisable()
81+
{
82+
EVENTS.remove(PacketOutputListener.class, this);
83+
EVENTS.remove(UpdateListener.class, this);
84+
85+
flushAll();
86+
}
87+
88+
@Override
89+
public void onSentPacket(PacketOutputEvent event)
90+
{
91+
if(flushing)
92+
return;
93+
94+
Packet<?> packet = event.getPacket();
95+
96+
if(!shouldLimit())
97+
{
98+
recordSent(System.currentTimeMillis());
99+
return;
100+
}
101+
102+
if(isKeepAlive(packet))
103+
{
104+
recordSent(System.currentTimeMillis());
105+
return;
106+
}
107+
108+
if(limit.getValueI() <= 0)
109+
{
110+
recordSent(System.currentTimeMillis());
111+
return;
112+
}
113+
114+
refillTokens();
115+
if(!queue.isEmpty())
116+
{
117+
queue.addLast(packet);
118+
event.cancel();
119+
return;
120+
}
121+
122+
if(tokens >= 1)
123+
{
124+
tokens -= 1;
125+
recordSent(System.currentTimeMillis());
126+
return;
127+
}
128+
129+
queue.addLast(packet);
130+
event.cancel();
131+
}
132+
133+
@Override
134+
public void onUpdate()
135+
{
136+
if(!shouldLimit())
137+
{
138+
queue.clear();
139+
tokens = 0;
140+
lastRefillMs = System.currentTimeMillis();
141+
pruneSentTimes(lastRefillMs);
142+
return;
143+
}
144+
145+
if(limit.getValueI() <= 0)
146+
{
147+
flushAll();
148+
pruneSentTimes(System.currentTimeMillis());
149+
return;
150+
}
151+
152+
refillTokens();
153+
sendQueuedPackets();
154+
pruneSentTimes(System.currentTimeMillis());
155+
}
156+
157+
private void sendQueuedPackets()
158+
{
159+
if(queue.isEmpty())
160+
return;
161+
162+
ClientPacketListener connection = MC.getConnection();
163+
if(connection == null)
164+
{
165+
queue.clear();
166+
return;
167+
}
168+
169+
flushing = true;
170+
try
171+
{
172+
while(!queue.isEmpty())
173+
{
174+
refillTokens();
175+
if(tokens < 1)
176+
break;
177+
178+
Packet<?> packet = queue.removeFirst();
179+
tokens -= 1;
180+
connection.send(packet);
181+
recordSent(System.currentTimeMillis());
182+
}
183+
184+
}finally
185+
{
186+
flushing = false;
187+
}
188+
}
189+
190+
private void flushAll()
191+
{
192+
if(queue.isEmpty())
193+
return;
194+
195+
ClientPacketListener connection = MC.getConnection();
196+
if(connection == null)
197+
{
198+
queue.clear();
199+
return;
200+
}
201+
202+
flushing = true;
203+
try
204+
{
205+
while(!queue.isEmpty())
206+
{
207+
connection.send(queue.removeFirst());
208+
recordSent(System.currentTimeMillis());
209+
}
210+
211+
}finally
212+
{
213+
flushing = false;
214+
}
215+
}
216+
217+
private void recordSent(long now)
218+
{
219+
sentTimes.addLast(now);
220+
pruneSentTimes(now);
221+
}
222+
223+
private void pruneSentTimes(long now)
224+
{
225+
long cutoff = now - 1000;
226+
while(!sentTimes.isEmpty() && sentTimes.peekFirst() <= cutoff)
227+
sentTimes.removeFirst();
228+
}
229+
230+
private void refillTokens()
231+
{
232+
int limitValue = limit.getValueI();
233+
if(limitValue <= 0)
234+
{
235+
lastRefillMs = System.currentTimeMillis();
236+
return;
237+
}
238+
239+
long now = System.currentTimeMillis();
240+
long elapsed = now - lastRefillMs;
241+
if(elapsed <= 0)
242+
return;
243+
244+
double refill = (elapsed / 1000D) * limitValue;
245+
tokens = Math.min(limitValue, tokens + refill);
246+
lastRefillMs = now;
247+
}
248+
249+
private boolean shouldLimit()
250+
{
251+
return limiterEnabled.isChecked() && MC.getConnection() != null;
252+
}
253+
254+
private static boolean isKeepAlive(Packet<?> packet)
255+
{
256+
String name = packet.getClass().getSimpleName();
257+
return "ServerboundKeepAlivePacket".equals(name)
258+
|| "ClientboundKeepAlivePacket".equals(name);
259+
}
260+
}

0 commit comments

Comments
 (0)