Skip to content

Commit 0f2088c

Browse files
committed
Updated AutoLibrarian
1 parent 4a7727d commit 0f2088c

2 files changed

Lines changed: 159 additions & 2 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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: 157 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();
@@ -299,6 +342,7 @@ private void placeJobSite()
299342
{
300343
System.out.println("Job site has been placed.");
301344
placingJobSite = false;
345+
professionRetryDelay = 40;
302346

303347
}else
304348
{
@@ -445,8 +489,7 @@ private void setTargetVillager()
445489
.filter(e -> !e.isRemoved()).filter(Villager.class::isInstance)
446490
.map(e -> (Villager)e).filter(e -> e.getHealth() > 0)
447491
.filter(e -> player.distanceToSqr(e) <= rangeSq)
448-
.filter(e -> e.getVillagerData().profession().unwrapKey()
449-
.orElse(null) == VillagerProfession.LIBRARIAN)
492+
.filter(this::isLibrarian)
450493
.filter(e -> e.getVillagerData().level() == 1)
451494
.filter(e -> !experiencedVillagers.contains(e));
452495

@@ -502,11 +545,114 @@ private void setTargetJobSite()
502545
System.out.println("Found lectern at " + jobSite);
503546
}
504547

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

511657
if(villager != null)
512658
RenderUtils.drawOutlinedBox(matrixStack, villager.getBoundingBox(),
@@ -521,6 +667,15 @@ public void onRender(PoseStack matrixStack, float partialTicks)
521667
RenderUtils.drawOutlinedBoxes(matrixStack, expVilBoxes, red, false);
522668
RenderUtils.drawCrossBoxes(matrixStack, expVilBoxes, red, false);
523669

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

0 commit comments

Comments
 (0)