Skip to content

Commit

Permalink
Add simulation for more actions and add trade cracking for levels abo…
Browse files Browse the repository at this point in the history
…ve 1
  • Loading branch information
Gaider10 committed Jun 20, 2024
1 parent 2734f64 commit 47e53f2
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class VillagerCommand {
private static final SimpleCommandExceptionType NOT_A_VILLAGER_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.cvillager.notAVillager"));
private static final SimpleCommandExceptionType NO_CRACKED_VILLAGER_PRESENT = new SimpleCommandExceptionType(Component.translatable("commands.cvillager.noCrackedVillagerPresent"));
private static final SimpleCommandExceptionType NO_PROFESSION = new SimpleCommandExceptionType(Component.translatable("commands.cvillager.noProfession"));
private static final SimpleCommandExceptionType NOT_LEVEL_1 = new SimpleCommandExceptionType(Component.translatable("commands.cvillager.notLevel1"));
private static final Dynamic2CommandExceptionType INVALID_GOAL_INDEX = new Dynamic2CommandExceptionType((a, b) -> Component.translatable("commands.cvillager.removeGoal.invalidIndex", a, b));
public static final List<Goal> goals = new ArrayList<>();

Expand Down Expand Up @@ -79,7 +80,10 @@ public static void register(CommandDispatcher<FabricClientCommandSource> dispatc
.then(argument("pos", blockPos())
.executes(ctx -> setClockPos(getBlockPos(ctx, "pos")))))
.then(literal("brute-force")
.executes(ctx -> bruteForce())));
.then(literal("first-level")
.executes(ctx -> bruteForce(false)))
.then(literal("next-level")
.executes(ctx -> bruteForce(true)))));
}

private static int addGoal(WithStringArgument.Result<CItemStackPredicateArgument> firstPredicate, WithStringArgument.Result<MinMaxBounds.Ints> firstItemQuantityRange, @Nullable WithStringArgument.Result<CItemStackPredicateArgument> secondPredicate, @Nullable WithStringArgument.Result<MinMaxBounds.Ints> secondItemQuantityRange, WithStringArgument.Result<CItemStackPredicateArgument> resultPredicate, WithStringArgument.Result<MinMaxBounds.Ints> resultItemQuantityRange) {
Expand Down Expand Up @@ -146,7 +150,7 @@ private static int setClockPos(BlockPos pos) {
return Command.SINGLE_SUCCESS;
}

private static int bruteForce() throws CommandSyntaxException {
private static int bruteForce(boolean levelUp) throws CommandSyntaxException {
Villager targetVillager = VillagerCracker.getVillager();

if (!(targetVillager instanceof IVillager iVillager) || iVillager.clientcommands_getCrackedRandom() == null) {
Expand All @@ -157,12 +161,19 @@ private static int bruteForce() throws CommandSyntaxException {
throw NO_PROFESSION.create();
}

VillagerTrades.ItemListing[] listings = VillagerTrades.TRADES.get(profession).getOrDefault(1, new VillagerTrades.ItemListing[0]);
int adjustment = 2 + Configs.villagerAdjustment;
Pair<Integer, Offer> pair = iVillager.clientcommands_bruteForceOffers(listings, profession, Configs.maxVillagerBruteForceSimulationCalls, offer -> VillagerCommand.goals.stream().anyMatch(goal -> goal.matches(offer.first(), offer.second(), offer.result()))).mapFirst(x -> x + adjustment);
int ticks = pair.getFirst();
int currentLevel = targetVillager.getVillagerData().getLevel();
if (!levelUp && currentLevel != 1) {
throw NOT_LEVEL_1.create();
}

int crackedLevel = levelUp ? currentLevel + 1 : currentLevel;

VillagerTrades.ItemListing[] listings = VillagerTrades.TRADES.get(profession).getOrDefault(crackedLevel, new VillagerTrades.ItemListing[0]);
int adjustment = 2 + Configs.villagerAdjustment + (levelUp ? -80 : 0);
Pair<Integer, Offer> pair = iVillager.clientcommands_bruteForceOffers(listings, profession, levelUp ? 240 : 0, Configs.maxVillagerBruteForceSimulationCalls, offer -> VillagerCommand.goals.stream().anyMatch(goal -> goal.matches(offer.first(), offer.second(), offer.result()))).mapFirst(x -> x + adjustment);
int calls = pair.getFirst();
Offer offer = pair.getSecond();
if (ticks < 0) {
if (calls < 0) {
VillagerRngSimulator.CrackedState state = iVillager.clientcommands_getCrackedRandom().getCrackedState();
Component message = state.getMessage(true);
if (state.isCracked()) {
Expand All @@ -176,10 +187,10 @@ private static int bruteForce() throws CommandSyntaxException {
} else {
price = VillagerCommand.displayText(offer.first()) + " + " + VillagerCommand.displayText(offer.second());
}
ClientCommandHelper.sendFeedback(Component.translatable("commands.cvillager.bruteForce.success", VillagerCommand.displayText(offer.result()), price, ticks).withStyle(ChatFormatting.GREEN));
ClientCommandHelper.sendFeedback(Component.translatable("commands.cvillager.bruteForce.success", VillagerCommand.displayText(offer.result()), price, calls).withStyle(ChatFormatting.GREEN));
VillagerCracker.targetOffer = offer;
System.out.println(offer);
iVillager.clientcommands_getCrackedRandom().setCallsUntilOpenGui(ticks, offer.result());
iVillager.clientcommands_getCrackedRandom().setCallsUntilToggleGui(calls, offer.result());
}

return Command.SINGLE_SUCCESS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import net.earthcomputer.clientcommands.interfaces.IVillager;
import net.minecraft.client.Minecraft;
import net.minecraft.core.GlobalPos;
import net.minecraft.network.protocol.game.ClientboundAddExperienceOrbPacket;
import net.minecraft.network.protocol.game.ClientboundSoundPacket;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.npc.Villager;
Expand Down Expand Up @@ -76,10 +77,20 @@ public static void onSoundEventPlayed(ClientboundSoundPacket packet) {
switch (packet.getSound().value().getLocation().toString()) {
case "minecraft:entity.villager.ambient" -> ((IVillager) targetVillager).clientcommands_onAmbientSoundPlayed(packet.getPitch());
case "minecraft:entity.villager.no" -> ((IVillager) targetVillager).clientcommands_onNoSoundPlayed(packet.getPitch());
case "minecraft:entity.villager.yes" -> ((IVillager) targetVillager).clientcommands_onYesSoundPlayed(packet.getPitch());
case "minecraft:entity.generic.splash" -> ((IVillager) targetVillager).clientcommands_onSplashSoundPlayed(packet.getPitch());
}
}

public static void onXpOrbSpawned(ClientboundAddExperienceOrbPacket packet) {
Villager targetVillager = getVillager();
if (targetVillager == null) {
return;
}

((IVillager) targetVillager).clientcommands_onXpOrbSpawned(packet.getValue());
}

public static void onServerTick() {
Villager targetVillager = getVillager();
if (targetVillager == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ public LegacyRandomSource getRandom() {
return random;
}

public void setCallsUntilOpenGui(int calls, ItemStack resultStack) {
public void setCallsUntilToggleGui(int calls, ItemStack resultStack) {
callsAtStartOfBruteForce = totalCalls;
callsInBruteForce = calls;
activeGoalResult = resultStack;
Expand Down Expand Up @@ -323,11 +323,30 @@ public void onAmbientSoundPlayed(float pitch) {
}
}

public void onNoSoundPlayed(float pitch) {
public void onNoSoundPlayed(float pitch, boolean fromGuiInteract) {
// the last received action before the next tick's clock
// played both when interacting with a villager without a profession and when using the villager gui

if (random != null) {
totalCalls += 2;
if (fromGuiInteract) {
ambientSoundTime = -80;
}
float simulatedPitch = (random.nextFloat() - random.nextFloat()) * 0.2f + 1.0f;
if (pitch != simulatedPitch) {
ClientCommandHelper.addOverlayMessage(Component.translatable("commands.cvillager.outOfSync").withStyle(ChatFormatting.RED), 100);
reset();
}
}
}

public void onYesSoundPlayed(float pitch) {
// the last received action before the next tick's clock
// played when using the villager gui

if (random != null) {
totalCalls += 2;
ambientSoundTime = -80;
float simulatedPitch = (random.nextFloat() - random.nextFloat()) * 0.2f + 1.0f;
if (pitch != simulatedPitch) {
ClientCommandHelper.addOverlayMessage(Component.translatable("commands.cvillager.outOfSync").withStyle(ChatFormatting.RED), 100);
Expand Down Expand Up @@ -359,6 +378,22 @@ public void onSplashSoundPlayed(float pitch) {
}
}

public void onXpOrbSpawned(int value) {
// the last received action before the next tick's clock

if (random != null) {
totalCalls += 1;
ambientSoundTime = -80;
int simulatedValue = 3 + this.random.nextInt(4);
boolean leveledUp = value > 3 + 3;
if (leveledUp) simulatedValue += 5;
if (value != simulatedValue) {
ClientCommandHelper.addOverlayMessage(Component.translatable("commands.cvillager.outOfSync").withStyle(ChatFormatting.RED), 100);
reset();
}
}
}

public long[] crackSeed() {
if (!(80 <= ticksBetweenSounds && ticksBetweenSounds - 80 < LATTICES.length)) {
return new long[0];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ public interface IVillager {

void clientcommands_onNoSoundPlayed(float pitch);

void clientcommands_onYesSoundPlayed(float pitch);

void clientcommands_onSplashSoundPlayed(float pitch);

void clientcommands_onXpOrbSpawned(int value);

void clientcommands_onServerTick();

Pair<Integer, VillagerCommand.Offer> clientcommands_bruteForceOffers(VillagerTrades.ItemListing[] listings, VillagerProfession profession, int maxCalls, Predicate<VillagerCommand.Offer> predicate);
Pair<Integer, VillagerCommand.Offer> clientcommands_bruteForceOffers(VillagerTrades.ItemListing[] listings, VillagerProfession profession, int minTicks, int maxCalls, Predicate<VillagerCommand.Offer> predicate);
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package net.earthcomputer.clientcommands.mixin.commands.villager;

import com.mojang.datafixers.util.Pair;
import net.earthcomputer.clientcommands.command.ClientCommandHelper;
import net.earthcomputer.clientcommands.command.VillagerCommand;
import net.earthcomputer.clientcommands.features.VillagerRngSimulator;
import net.earthcomputer.clientcommands.interfaces.IVillager;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.client.gui.screens.inventory.MerchantScreen;
import net.minecraft.util.RandomSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
Expand Down Expand Up @@ -53,35 +49,56 @@ public void clientcommands_onAmbientSoundPlayed(float pitch) {

@Override
public void clientcommands_onNoSoundPlayed(float pitch) {
rng.onNoSoundPlayed(pitch);
VillagerProfession profession = this.getVillagerData().getProfession();
rng.onNoSoundPlayed(pitch, profession != VillagerProfession.NONE && profession != VillagerProfession.NITWIT);
}

@Override
public void clientcommands_onYesSoundPlayed(float pitch) {
rng.onYesSoundPlayed(pitch);
}

@Override
public void clientcommands_onSplashSoundPlayed(float pitch) {
rng.onSplashSoundPlayed(pitch);
}

@Override
public void clientcommands_onXpOrbSpawned(int value) {
rng.onXpOrbSpawned(value);
}

@Override
public void clientcommands_onServerTick() {
rng.simulateTick();

if (rng.shouldInteractWithVillager()) {
Minecraft minecraft = Minecraft.getInstance();
InteractionResult result = minecraft.gameMode.interact(minecraft.player, this, InteractionHand.MAIN_HAND);
if (result.consumesAction() && result.shouldSwing()) {
minecraft.player.swing(InteractionHand.MAIN_HAND);
if (minecraft.screen instanceof MerchantScreen) {
minecraft.screen.onClose();
} else {
InteractionResult result = minecraft.gameMode.interact(minecraft.player, this, InteractionHand.MAIN_HAND);
if (result.consumesAction() && result.shouldSwing()) {
minecraft.player.swing(InteractionHand.MAIN_HAND);
}
}
}
}

@Override
public Pair<Integer, VillagerCommand.Offer> clientcommands_bruteForceOffers(VillagerTrades.ItemListing[] listings, VillagerProfession profession, int maxCalls, Predicate<VillagerCommand.Offer> predicate) {
public Pair<Integer, VillagerCommand.Offer> clientcommands_bruteForceOffers(VillagerTrades.ItemListing[] listings, VillagerProfession profession, int minTicks, int maxCalls, Predicate<VillagerCommand.Offer> predicate) {
if (this instanceof IVillager iVillager && iVillager.clientcommands_getCrackedRandom().getCrackedState().isCracked()) {
VillagerProfession oldProfession = getVillagerData().getProfession();
setVillagerData(getVillagerData().setProfession(profession));

VillagerRngSimulator rng = this.rng.copy();

int startingCalls = rng.getTotalCalls();

for (int i = 0; i < minTicks; i++) {
rng.simulateTick();
}

while (rng.getTotalCalls() < maxCalls + startingCalls) {
VillagerRngSimulator randomBranch = rng.copy();
randomBranch.simulateTick();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.core.GlobalPos;
import net.minecraft.network.protocol.game.ClientboundAddExperienceOrbPacket;
import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundSoundPacket;
Expand Down Expand Up @@ -54,6 +55,14 @@ private void onHandleChunkBlocksUpdate(ClientboundSectionBlocksUpdatePacket pack
}
}

@Inject(method = "handleAddExperienceOrb", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/network/protocol/PacketUtils;ensureRunningOnSameThread(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketListener;Lnet/minecraft/util/thread/BlockableEventLoop;)V"))
private void onHandleAddExperienceOrb(ClientboundAddExperienceOrbPacket packet, CallbackInfo ci) {
Villager targetVillager = VillagerCracker.getVillager();
if (targetVillager != null && new Vec3(packet.getX(), packet.getY() - 0.5, packet.getZ()).distanceToSqr(targetVillager.position()) <= 0.1f) {
VillagerCracker.onXpOrbSpawned(packet);
}
}

@Inject(method = "handleBlockUpdate", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/network/protocol/PacketUtils;ensureRunningOnSameThread(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketListener;Lnet/minecraft/util/thread/BlockableEventLoop;)V"))
private void onHandleBlockUpdate(ClientboundBlockUpdatePacket packet, CallbackInfo ci) {
if (Minecraft.getInstance().level != null && new GlobalPos(Minecraft.getInstance().level.dimension(), packet.getPos()).equals(VillagerCracker.getClockPos())) {
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/assets/clientcommands/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@
"commands.cvillager.listGoals.success": "There are %d villager goals:",
"commands.cvillager.noCrackedVillagerPresent": "There was no cracked villager available to use",
"commands.cvillager.noProfession": "The targeted villager has no profession.",
"commands.cvillager.notLevel1": "The targeted villager has level above 1.",
"commands.cvillager.notAVillager": "Target was not a villager",
"commands.cvillager.outOfSync": "Your Villager's RNG was out of sync, re-cracking.",
"commands.cvillager.removeGoal.invalidIndex": "Unable to remove goal %d, you only have %d goals.",
Expand Down

0 comments on commit 47e53f2

Please sign in to comment.