Skip to content

Commit 186b3e8

Browse files
committed
Updated AutoLibrarian and GameStats
1 parent 4a7727d commit 186b3e8

4 files changed

Lines changed: 185 additions & 3 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -764,7 +764,7 @@ This hack is still undergoing development and has only been tested in the end. A
764764
![Hearts](https://i.imgur.com/FZ8Z0TK.png)
765765

766766
### GameStats
767-
- On-screen HUD with configurable stats: FPS, TPS, Ping, Play Time, Time, Packet Rate (in/out), Distance, Mob Kills, Player Kills, and XP Gained
767+
- On-screen HUD with configurable stats: FPS, TPS, Ping, Play Time, Time, MC World Time, Packet Rate (in/out), Distance, Mob Kills, Player Kills, and XP Gained
768768
- Draggable/repositionable while inventory or chests screens are open
769769
- FPS/TPS/Ping show running averages in brackets (e.g. `Ping: 30 (21)`)
770770
- Style settings: font size, font opacity, font color, font stroke, background box toggle, background color, and background opacity
@@ -950,6 +950,8 @@ Examples:
950950
### AutoLibrarian Improved
951951
- Can now discover enchantments provided by data packs.
952952
- Able to search for enchantments by keywords.
953+
- Reasons for failure indicated in chat.
954+
- Will auto-break lecturn if they refuse the job.
953955

954956
![Library](https://i.imgur.com/pWqgNz8.png)
955957

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

Lines changed: 158 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@
2020
import net.minecraft.client.multiplayer.MultiPlayerGameMode;
2121
import net.minecraft.client.player.LocalPlayer;
2222
import net.minecraft.core.BlockPos;
23+
import net.minecraft.core.GlobalPos;
2324
import net.minecraft.core.Holder;
2425
import net.minecraft.network.protocol.game.ServerboundSelectTradePacket;
26+
import net.minecraft.resources.ResourceKey;
2527
import net.minecraft.world.InteractionHand;
2628
import net.minecraft.world.InteractionResult;
29+
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
2730
import net.minecraft.world.entity.npc.villager.Villager;
2831
import net.minecraft.world.entity.npc.villager.VillagerProfession;
2932
import net.minecraft.world.inventory.ClickType;
@@ -109,9 +112,13 @@ public final class AutoLibrarianHack extends Hack
109112

110113
private Villager villager;
111114
private BlockPos jobSite;
115+
private BlockPos blockingJobSite;
112116

113117
private boolean placingJobSite;
114118
private boolean breakingJobSite;
119+
private int professionRetryDelay;
120+
private int reasonMessageCooldown;
121+
private String lastReasonMessage = "";
115122

116123
public AutoLibrarianHack()
117124
{
@@ -149,8 +156,12 @@ protected void onDisable()
149156
overlay.resetProgress();
150157
villager = null;
151158
jobSite = null;
159+
blockingJobSite = null;
152160
placingJobSite = false;
153161
breakingJobSite = false;
162+
professionRetryDelay = 0;
163+
reasonMessageCooldown = 0;
164+
lastReasonMessage = "";
154165
experiencedVillagers.clear();
155166
}
156167

@@ -185,6 +196,38 @@ public void onUpdate()
185196
return;
186197
}
187198

199+
// If the villager still hasn't taken the librarian job, keep cycling
200+
// the lectern until it does. This avoids getting stuck probing forever.
201+
if(!isLibrarian(villager))
202+
{
203+
updateBlockingJobSite();
204+
chatNoLibrarianReason();
205+
if(professionRetryDelay > 0)
206+
{
207+
professionRetryDelay--;
208+
return;
209+
}
210+
211+
if(BlockUtils.getBlock(jobSite) == Blocks.LECTERN)
212+
{
213+
breakingJobSite = true;
214+
ChatUtils.message(
215+
"Villager is not a librarian yet. Replacing lectern...");
216+
217+
}else
218+
{
219+
placingJobSite = true;
220+
ChatUtils.message(
221+
"Villager is not a librarian yet. Placing lectern...");
222+
}
223+
224+
return;
225+
}
226+
227+
blockingJobSite = null;
228+
lastReasonMessage = "";
229+
reasonMessageCooldown = 0;
230+
188231
if(!(MC.screen instanceof MerchantScreen tradeScreen))
189232
{
190233
openTradeScreen();
@@ -257,6 +300,13 @@ public void onUpdate()
257300
setEnabled(false);
258301
}
259302

303+
@Override
304+
public String getRenderName()
305+
{
306+
int remaining = wantedBooks.getOffers().size();
307+
return getName() + " [" + remaining + " books]";
308+
}
309+
260310
private void breakJobSite()
261311
{
262312
if(jobSite == null)
@@ -299,6 +349,7 @@ private void placeJobSite()
299349
{
300350
System.out.println("Job site has been placed.");
301351
placingJobSite = false;
352+
professionRetryDelay = 40;
302353

303354
}else
304355
{
@@ -445,8 +496,7 @@ private void setTargetVillager()
445496
.filter(e -> !e.isRemoved()).filter(Villager.class::isInstance)
446497
.map(e -> (Villager)e).filter(e -> e.getHealth() > 0)
447498
.filter(e -> player.distanceToSqr(e) <= rangeSq)
448-
.filter(e -> e.getVillagerData().profession().unwrapKey()
449-
.orElse(null) == VillagerProfession.LIBRARIAN)
499+
.filter(this::isLibrarian)
450500
.filter(e -> e.getVillagerData().level() == 1)
451501
.filter(e -> !experiencedVillagers.contains(e));
452502

@@ -502,11 +552,108 @@ private void setTargetJobSite()
502552
System.out.println("Found lectern at " + jobSite);
503553
}
504554

555+
private boolean isLibrarian(Villager villager)
556+
{
557+
return villager.getVillagerData().profession().unwrapKey()
558+
.orElse(null) == VillagerProfession.LIBRARIAN;
559+
}
560+
561+
private void updateBlockingJobSite()
562+
{
563+
BlockPos found = getVillagerMemoryPos(MemoryModuleType.JOB_SITE);
564+
if(found != null && !found.equals(jobSite))
565+
{
566+
blockingJobSite = found;
567+
return;
568+
}
569+
570+
found = getVillagerMemoryPos(MemoryModuleType.POTENTIAL_JOB_SITE);
571+
if(found != null && !found.equals(jobSite))
572+
{
573+
blockingJobSite = found;
574+
return;
575+
}
576+
577+
blockingJobSite = null;
578+
}
579+
580+
private void chatNoLibrarianReason()
581+
{
582+
if(villager == null || jobSite == null || MC.level == null)
583+
return;
584+
585+
if(reasonMessageCooldown > 0)
586+
{
587+
reasonMessageCooldown--;
588+
return;
589+
}
590+
591+
ResourceKey<VillagerProfession> profession =
592+
villager.getVillagerData().profession().unwrapKey().orElse(null);
593+
String reason;
594+
595+
if(profession == VillagerProfession.NITWIT)
596+
{
597+
reason = "Reason: villager is a nitwit and cannot take jobs.";
598+
599+
}else if(profession != VillagerProfession.NONE
600+
&& profession != VillagerProfession.LIBRARIAN)
601+
{
602+
String profName = String.valueOf(profession);
603+
reason = "Reason: villager already has a different profession ("
604+
+ profName + ").";
605+
606+
}else if(villager.getVillagerData().level() > 1)
607+
{
608+
reason =
609+
"Reason: villager level is above novice and profession is locked.";
610+
611+
}else if(villager.getVillagerXp() > 0)
612+
{
613+
reason =
614+
"Reason: villager has XP, so profession/trades are already locked.";
615+
616+
}else if(blockingJobSite != null)
617+
{
618+
reason = "Reason: villager remembers another workstation at "
619+
+ blockingJobSite.toShortString()
620+
+ " (yellow ESP/tracer). Break it.";
621+
622+
}else
623+
{
624+
reason =
625+
"Reason: likely pathing issue. Make sure villager can walk to the lectern.";
626+
}
627+
628+
if(!reason.equals(lastReasonMessage))
629+
{
630+
ChatUtils.message(reason);
631+
lastReasonMessage = reason;
632+
reasonMessageCooldown = 20;
633+
return;
634+
}
635+
636+
reasonMessageCooldown = 100;
637+
}
638+
639+
private BlockPos getVillagerMemoryPos(MemoryModuleType<GlobalPos> memory)
640+
{
641+
if(villager == null || MC.level == null)
642+
return null;
643+
644+
GlobalPos pos = villager.getBrain().getMemory(memory).orElse(null);
645+
if(pos == null || pos.dimension() != MC.level.dimension())
646+
return null;
647+
648+
return pos.pos();
649+
}
650+
505651
@Override
506652
public void onRender(PoseStack matrixStack, float partialTicks)
507653
{
508654
int green = 0xC000FF00;
509655
int red = 0xC0FF0000;
656+
int yellow = 0xC0FFFF00;
510657

511658
if(villager != null)
512659
RenderUtils.drawOutlinedBox(matrixStack, villager.getBoundingBox(),
@@ -521,6 +668,15 @@ public void onRender(PoseStack matrixStack, float partialTicks)
521668
RenderUtils.drawOutlinedBoxes(matrixStack, expVilBoxes, red, false);
522669
RenderUtils.drawCrossBoxes(matrixStack, expVilBoxes, red, false);
523670

671+
if(blockingJobSite != null)
672+
{
673+
AABB box = new AABB(blockingJobSite);
674+
RenderUtils.drawOutlinedBox(matrixStack, box, yellow, false);
675+
RenderUtils.drawCrossBox(matrixStack, box, yellow, false);
676+
RenderUtils.drawTracer(matrixStack, partialTicks,
677+
Vec3.atCenterOf(blockingJobSite), yellow, false);
678+
}
679+
524680
if(breakingJobSite)
525681
overlay.render(matrixStack, partialTicks, jobSite);
526682
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ public final class GameStatsHack extends Hack implements PacketInputListener,
5454
private final CheckboxSetting showCurrentTime =
5555
new CheckboxSetting("Show Time", true);
5656

57+
private final CheckboxSetting showWorldTime =
58+
new CheckboxSetting("Show World Time", true);
59+
5760
private final CheckboxSetting showPacketRate =
5861
new CheckboxSetting("Show Packet Rate", true);
5962

@@ -115,6 +118,7 @@ public GameStatsHack()
115118
addSetting(showPing);
116119
addSetting(showPlayTime);
117120
addSetting(showCurrentTime);
121+
addSetting(showWorldTime);
118122
addSetting(showPacketRate);
119123
addSetting(showDistanceTravelled);
120124
addSetting(showMobKills);
@@ -269,6 +273,11 @@ public boolean showCurrentTime()
269273
return showCurrentTime.isChecked();
270274
}
271275

276+
public boolean showWorldTime()
277+
{
278+
return showWorldTime.isChecked();
279+
}
280+
272281
public boolean showPacketRate()
273282
{
274283
return showPacketRate.isChecked();

src/main/java/net/wurstclient/hud/GameStatsHud.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ private List<String> buildLines()
190190
lines.add(withPrefix(showPrefixes, "Time",
191191
LocalTime.now().format(TIME_FORMAT)));
192192

193+
if(hack.showWorldTime())
194+
lines
195+
.add(withPrefix(showPrefixes, "World Time", getWorldTime24h()));
196+
193197
if(hack.showPacketRate())
194198
lines.add(
195199
withPrefix(showPrefixes, "Packets", hack.getIncomingPacketRate()
@@ -330,6 +334,17 @@ private static String formatDistance(double meters)
330334
return String.format(Locale.ROOT, "%.1f", meters);
331335
}
332336

337+
private static String getWorldTime24h()
338+
{
339+
if(MC.level == null)
340+
return "--:--";
341+
342+
long dayTime = Math.floorMod(MC.level.getDayTime(), 24000L);
343+
int hour = (int)(dayTime / 1000L);
344+
int minute = (int)Math.floor((dayTime % 1000L) * 60D / 1000D);
345+
return String.format(Locale.ROOT, "%02d:%02d", hour, minute);
346+
}
347+
333348
private static String withPrefix(boolean showPrefix, String prefix,
334349
String value)
335350
{

0 commit comments

Comments
 (0)