Skip to content

Commit 0127442

Browse files
committed
Improve MaceDMG & DamageESP
1 parent 478f63a commit 0127442

4 files changed

Lines changed: 161 additions & 4 deletions

File tree

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

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
import com.mojang.math.Axis;
2323

2424
import net.minecraft.client.gui.Font;
25+
import net.minecraft.core.registries.BuiltInRegistries;
2526
import net.minecraft.network.protocol.Packet;
27+
import net.minecraft.network.protocol.game.ClientboundDamageEventPacket;
2628
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket;
2729
import net.minecraft.util.Mth;
2830
import net.minecraft.util.RandomSource;
@@ -42,6 +44,7 @@
4244
import net.wurstclient.settings.ColorSetting;
4345
import net.wurstclient.settings.SliderSetting;
4446
import net.wurstclient.settings.SliderSetting.ValueDisplay;
47+
import net.wurstclient.util.ChatUtils;
4548
import net.wurstclient.util.RenderUtils;
4649

4750
@SearchTags({"damage esp", "damage numbers", "damage popups"})
@@ -71,7 +74,13 @@ public final class DamageEspHack extends Hack
7174
private final ColorSetting textColor = new ColorSetting("Text color",
7275
"Color of the floating damage numbers.", new Color(0xFF5555));
7376

77+
private final CheckboxSetting chatInfo = new CheckboxSetting("Chat info",
78+
"Show damage dealt by you in chat, including three-second attack burst summaries.",
79+
false);
80+
7481
private final Map<UUID, Float> trackedHealth = new HashMap<>();
82+
private final Map<Integer, PendingDamage> pendingDamage = new HashMap<>();
83+
private final Map<UUID, DamageBurst> damageBursts = new HashMap<>();
7584
private final List<DamagePopup> popups = new ArrayList<>();
7685
private final RandomSource random = RandomSource.create();
7786
private static final int HEALTH_DATA_ID = resolveHealthDataId();
@@ -85,13 +94,16 @@ public DamageEspHack()
8594
addSetting(fadeTime);
8695
addSetting(opacity);
8796
addSetting(textColor);
97+
addSetting(chatInfo);
8898
}
8999

90100
@Override
91101
protected void onEnable()
92102
{
93103
trackedHealth.clear();
94104
popups.clear();
105+
pendingDamage.clear();
106+
damageBursts.clear();
95107
EVENTS.add(UpdateListener.class, this);
96108
EVENTS.add(RenderListener.class, this);
97109
EVENTS.add(PacketInputListener.class, this);
@@ -105,6 +117,8 @@ protected void onDisable()
105117
EVENTS.remove(PacketInputListener.class, this);
106118
trackedHealth.clear();
107119
popups.clear();
120+
pendingDamage.clear();
121+
damageBursts.clear();
108122
}
109123

110124
@Override
@@ -116,6 +130,8 @@ public void onReceivedPacket(PacketInputEvent event)
116130

117131
if(packet instanceof ClientboundSetEntityDataPacket entityData)
118132
handleEntityDataPacket(entityData);
133+
else if(packet instanceof ClientboundDamageEventPacket damage)
134+
handleDamageEvent(damage);
119135
}
120136

121137
@Override
@@ -125,10 +141,15 @@ public void onUpdate()
125141
{
126142
trackedHealth.clear();
127143
popups.clear();
144+
pendingDamage.clear();
145+
damageBursts.clear();
128146
return;
129147
}
130148

131149
long now = MC.level.getGameTime();
150+
flushDamageBursts(now);
151+
pendingDamage.entrySet()
152+
.removeIf(entry -> now - entry.getValue().createdTick > 5);
132153
Set<UUID> seen = new HashSet<>();
133154
for(Entity entity : MC.level.entitiesForRendering())
134155
{
@@ -226,11 +247,94 @@ private void handleEntityDataPacket(ClientboundSetEntityDataPacket packet)
226247
trackedHealth.put(id, newHealth);
227248
float damage = oldHealth - newHealth;
228249
if(damage > DAMAGE_EPSILON)
250+
{
229251
spawnPopup(living, damage);
252+
PendingDamage pending = pendingDamage.remove(entityId);
253+
if(pending != null
254+
&& pending.createdTick + 5 >= MC.level.getGameTime())
255+
recordChatDamage(living, damage, pending.weapon);
256+
}
230257
break;
231258
}
232259
}
233260

261+
private void handleDamageEvent(ClientboundDamageEventPacket packet)
262+
{
263+
if(!chatInfo.isChecked() || MC.player == null || MC.level == null
264+
|| packet.sourceCauseId() != MC.player.getId())
265+
return;
266+
267+
Entity target = MC.level.getEntity(packet.entityId());
268+
if(!(target instanceof LivingEntity living) || !shouldTrack(living))
269+
return;
270+
271+
String weapon = getHeldItemName();
272+
pendingDamage.put(packet.entityId(),
273+
new PendingDamage(weapon, MC.level.getGameTime()));
274+
}
275+
276+
private void recordChatDamage(LivingEntity target, float damage,
277+
String weapon)
278+
{
279+
if(!chatInfo.isChecked())
280+
return;
281+
282+
String amount = formatDamage(damage);
283+
ChatUtils.message("You did " + amount + " HP damage to "
284+
+ target.getName().getString() + " with " + weapon + ".");
285+
286+
UUID id = target.getUUID();
287+
DamageBurst burst = damageBursts.computeIfAbsent(id,
288+
ignored -> new DamageBurst(target.getName().getString(), weapon));
289+
burst.totalDamage += damage;
290+
if(burst.hitCount > 0)
291+
burst.totalIntervalTicks +=
292+
MC.level.getGameTime() - burst.lastHitTick;
293+
burst.hitCount++;
294+
burst.lastHitTick = MC.level.getGameTime();
295+
burst.weapon = weapon;
296+
}
297+
298+
private void flushDamageBursts(long now)
299+
{
300+
if(!chatInfo.isChecked())
301+
{
302+
damageBursts.clear();
303+
return;
304+
}
305+
306+
damageBursts.entrySet().removeIf(entry -> {
307+
DamageBurst burst = entry.getValue();
308+
if(now - burst.lastHitTick < 60)
309+
return false;
310+
if(burst.hitCount > 1)
311+
ChatUtils.message("Attack summary on " + burst.targetName + ": "
312+
+ burst.hitCount + " hits, average "
313+
+ formatDamage(burst.totalDamage / burst.hitCount)
314+
+ " HP, average time between hits "
315+
+ formatSeconds(
316+
burst.totalIntervalTicks / (burst.hitCount - 1))
317+
+ ", total " + formatDamage(burst.totalDamage) + " HP with "
318+
+ burst.weapon + ".");
319+
return true;
320+
});
321+
}
322+
323+
private String getHeldItemName()
324+
{
325+
if(MC.player == null || MC.player.getMainHandItem().isEmpty())
326+
return "UNKNOWN";
327+
328+
String name = BuiltInRegistries.ITEM
329+
.getKey(MC.player.getMainHandItem().getItem()).getPath();
330+
return name.replace('_', ' ').toUpperCase(Locale.ROOT);
331+
}
332+
333+
private String formatSeconds(double ticks)
334+
{
335+
return String.format(Locale.ROOT, "%.1f seconds", ticks / 20.0);
336+
}
337+
234338
private void spawnPopup(LivingEntity entity, float damage)
235339
{
236340
if(MC.level == null)
@@ -299,6 +403,25 @@ private double ageTicks(double currentTick)
299403
}
300404
}
301405

406+
private record PendingDamage(String weapon, long createdTick)
407+
{}
408+
409+
private static final class DamageBurst
410+
{
411+
private final String targetName;
412+
private String weapon;
413+
private float totalDamage;
414+
private int hitCount;
415+
private long lastHitTick;
416+
private long totalIntervalTicks;
417+
418+
private DamageBurst(String targetName, String weapon)
419+
{
420+
this.targetName = targetName;
421+
this.weapon = weapon;
422+
}
423+
}
424+
302425
private static int resolveHealthDataId()
303426
{
304427
try

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ public void update()
125125
"How high to fake before slamming. Height determines the damage boost.",
126126
DEFAULT_HEIGHT, 1.6, PAPER_MAX_HEIGHT, 0.1, ValueDisplay.DECIMAL);
127127

128+
private final CheckboxSetting attackCap = new CheckboxSetting(
129+
"6-block attack cap",
130+
"Blocks all entity attacks started while MaceDMG is enabled when the target"
131+
+ " is more than 6 blocks horizontally or vertically away.",
132+
true);
133+
128134
private final CheckboxSetting totemBypass = new CheckboxSetting(
129135
"Totem bypass",
130136
"Paper only. With MultiAura, sends a rising-damage mace burst against totem users.",
@@ -208,6 +214,7 @@ public MaceDmgHack()
208214
setCategory(Category.COMBAT);
209215
addSetting(serverType);
210216
addSetting(height);
217+
addSetting(attackCap);
211218
addSetting(totemBypass);
212219
addSetting(autoOptimize);
213220
addSetting(heightIncrease);
@@ -337,6 +344,21 @@ private boolean canAttemptSmash(Entity target)
337344
&& !living.isRemoved();
338345
}
339346

347+
public boolean shouldBlockAttack(Entity target)
348+
{
349+
if(!isEnabled() || !attackCap.isChecked() || MC.player == null
350+
|| target == null)
351+
return false;
352+
353+
AABB box = target.getBoundingBox();
354+
double x = Math.clamp(MC.player.getX(), box.minX, box.maxX);
355+
double z = Math.clamp(MC.player.getZ(), box.minZ, box.maxZ);
356+
double dx = MC.player.getX() - x;
357+
double dz = MC.player.getZ() - z;
358+
double verticalDistance = Math.abs(MC.player.getY() - target.getY());
359+
return dx * dx + dz * dz > 36.0 || verticalDistance > 6.0;
360+
}
361+
340362
public boolean hasFallDebt()
341363
{
342364
if(smashState == SmashState.IDLE)
@@ -670,7 +692,8 @@ private boolean performQueuedSmashBurst(Entity target)
670692
{
671693
if(target == null || MC.gameMode == null)
672694
return false;
673-
695+
if(shouldBlockAttack(target))
696+
return false;
674697
List<Double> offsets = getBurstOffsets(target);
675698
if(offsets.isEmpty() || !beginSmashAttempt(target))
676699
return false;

src/main/java/net/wurstclient/mixin/MultiPlayerGameModeMixin.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import net.wurstclient.events.PlayerAttacksEntityListener.PlayerAttacksEntityEvent;
4242
import net.wurstclient.events.StopUsingItemListener.StopUsingItemEvent;
4343
import net.wurstclient.hacks.AntiDropHack;
44+
import net.wurstclient.hacks.MaceDmgHack;
4445
import net.wurstclient.hacks.SilkOnlyHack;
4546
import net.wurstclient.mixinterface.IMultiPlayerGameMode;
4647

@@ -172,12 +173,23 @@ private void onClickSlotHEAD(int syncId, int slotId, int button,
172173
}
173174

174175
@Inject(at = @At("HEAD"),
175-
method = "attack(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/entity/Entity;)V")
176+
method = "attack(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/entity/Entity;)V",
177+
cancellable = true)
176178
private void onAttackEntity(Player player, Entity target, CallbackInfo ci)
177179
{
178180
if(player != minecraft.player)
179181
return;
180182

183+
if(WurstClient.INSTANCE.isEnabled())
184+
{
185+
MaceDmgHack maceDmg = WurstClient.INSTANCE.getHax().maceDmgHack;
186+
if(maceDmg != null && maceDmg.shouldBlockAttack(target))
187+
{
188+
ci.cancel();
189+
return;
190+
}
191+
}
192+
181193
EventManager.fire(new PlayerAttacksEntityEvent(target));
182194
}
183195

src/main/java/net/wurstclient/options/WurstOptionsScreen.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,7 @@ private void addCoreSection()
215215
addButton(column,
216216
() -> "Shader Background: "
217217
+ onOff(titleScreenShadertoyBackground.isChecked()),
218-
"Render an animatedbackground behind the title screen.",
219-
b -> {
218+
"Render an animatedbackground behind the title screen.", b -> {
220219
boolean wasEnabled = titleScreenShadertoyBackground.isChecked();
221220
titleScreenShadertoyBackground.setChecked(!wasEnabled);
222221
if(!wasEnabled)

0 commit comments

Comments
 (0)