diff --git a/src/main/java/net/cortex/clientAddon/cracker/SeedCracker.java b/src/main/java/net/cortex/clientAddon/cracker/SeedCracker.java index 22ba5b459..13e858b05 100644 --- a/src/main/java/net/cortex/clientAddon/cracker/SeedCracker.java +++ b/src/main/java/net/cortex/clientAddon/cracker/SeedCracker.java @@ -5,6 +5,7 @@ import net.earthcomputer.clientcommands.features.EnchantmentCracker; import net.earthcomputer.clientcommands.features.PlayerRandCracker; import net.earthcomputer.clientcommands.mixin.LegacyRandomSourceAccessor; +import net.earthcomputer.clientcommands.task.ItemThrowTask; import net.earthcomputer.clientcommands.task.LongTask; import net.earthcomputer.clientcommands.task.TaskManager; import net.minecraft.ChatFormatting; @@ -14,7 +15,6 @@ import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; import net.minecraft.util.RandomSource; -import net.minecraft.world.entity.EntityType; public class SeedCracker { public interface OnCrack {void callback(long seed); } @@ -26,23 +26,40 @@ public interface OnCrack {void callback(long seed); } public static LongTask currentTask; private static int attemptCount = 0; private static final int MAX_ATTEMPTS = 10; + private static String currentTaskName = null; - //returns True on success or false on failer - private static boolean throwItems() + private static String throwItems() { LocalPlayer player = Minecraft.getInstance().player; player.moveTo(player.getX(), player.getY(), player.getZ(), 0, 90); Minecraft.getInstance().getConnection().send(new ServerboundMovePlayerPacket.Rot(0, 90, true)); // point to correct location - for (int i = 0; i < 20; i++) { - boolean success = PlayerRandCracker.throwItem(); - if (!success) { + ItemThrowTask task = new ItemThrowTask(20) { + @Override + protected void onSuccess() { + SeedCracker.attemptCrack(); + } + + @Override + protected void onFailedToThrowItem() { Minecraft.getInstance().gui.getChat().addMessage(Component.translatable("itemCrack.notEnoughItems").withStyle(ChatFormatting.RED)); EnchantmentCracker.LOGGER.info("Unable to use rng SeedCracker |not enough items|"); - return false; + Configs.playerCrackState = PlayerRandCracker.CrackState.UNCRACKED; + currentTaskName = null; + } + + @Override + protected void onItemSpawn(ClientboundAddEntityPacket packet) { + onEntityCreation(packet); } + }; + if (currentTaskName != null) { + TaskManager.forceAddTask(currentTaskName, task); + return currentTaskName; + } else { + return TaskManager.addTask("ccrackrng", task); } - return true; } + public static void attemptCrack() { long seed= Lattice_cracker.crack(SeedCracker.bits); @@ -54,6 +71,7 @@ public static void attemptCrack() ClientCommandHelper.sendError(Component.translatable("commands.ccrackrng.failed")); ClientCommandHelper.sendHelp(Component.translatable("commands.ccrackrng.failed.help")); Configs.playerCrackState = PlayerRandCracker.CrackState.UNCRACKED; + currentTaskName = null; } else { SeedCracker.doCrack(SeedCracker.callback); } @@ -77,6 +95,7 @@ public static void attemptCrack() System.out.print(padLeftZeros(Long.toBinaryString((((long) (rand.nextFloat() * ((float) (1 << 24)))) >> (24 - 4))&0xFL), 4)+" \n"); }*/ + currentTaskName = null; callback.callback(((LegacyRandomSourceAccessor) rand).getSeed().get());//extract seed and call callback } @@ -88,35 +107,25 @@ public static void crack(OnCrack callback) { private static void doCrack(OnCrack Callback){ callback=Callback; ClientCommandHelper.addOverlayMessage(Component.translatable("commands.ccrackrng.retries", attemptCount, MAX_ATTEMPTS), 100); - if(throwItems()) - { - Configs.playerCrackState = PlayerRandCracker.CrackState.CRACKING; - expectedItems=20; - if (currentTask == null) { - currentTask = new SeedCrackTask(); - String taskName = TaskManager.addTask("ccrackrng", currentTask); - Component message = Component.translatable("commands.ccrackrng.starting") - .append(" ") - .append(ClientCommandHelper.getCommandTextComponent("commands.client.cancel", "/ctask stop " + taskName)); - Minecraft.getInstance().gui.getChat().addMessage(message); - } - } else { - Configs.playerCrackState = PlayerRandCracker.CrackState.UNCRACKED; + currentTaskName = throwItems(); + Configs.playerCrackState = PlayerRandCracker.CrackState.CRACKING; + expectedItems = 20; + if (attemptCount == 1) { + Component message = Component.translatable("commands.ccrackrng.starting") + .append(" ") + .append(ClientCommandHelper.getCommandTextComponent("commands.client.cancel", "/ctask stop " + currentTaskName)); + Minecraft.getInstance().gui.getChat().addMessage(message); } } public static void onEntityCreation(ClientboundAddEntityPacket packet) { - if (packet.getType() == EntityType.ITEM && Configs.playerCrackState == PlayerRandCracker.CrackState.CRACKING) { + if (Configs.playerCrackState == PlayerRandCracker.CrackState.CRACKING) { if (SeedCracker.expectedItems > 0) { long rand_val = (long) ((Math.atan2(packet.getZa(), packet.getXa()) + Math.PI) / (Math.PI * 2) * ((float) (1 << 24))); long top_bits = rand_val; short value = (short) (((top_bits >> (24 - 4)) ^ 0x8L )&0xFL);//INSTEAD OF ^0x8L MAYBE DO +math.pi OR SOMETHING ELSE SeedCracker.bits[20-SeedCracker.expectedItems]=(long)value;//could be improved SeedCracker.expectedItems--; - if (SeedCracker.expectedItems == 0) { - //if its the last item - SeedCracker.attemptCrack(); - } } } } diff --git a/src/main/java/net/earthcomputer/clientcommands/Configs.java b/src/main/java/net/earthcomputer/clientcommands/Configs.java index 8dc91b7b8..10ccbecda 100644 --- a/src/main/java/net/earthcomputer/clientcommands/Configs.java +++ b/src/main/java/net/earthcomputer/clientcommands/Configs.java @@ -110,6 +110,9 @@ public static void setMaxChorusItemThrows(int maxChorusItemThrows) { @Config public static boolean acceptC2CPackets = false; + @Config + public static float itemThrowsPerTick = 1; + public static boolean conditionLessThan1_20() { return MultiVersionCompat.INSTANCE.getProtocolVersion() < MultiVersionCompat.V1_20; } diff --git a/src/main/java/net/earthcomputer/clientcommands/features/EnchantmentCracker.java b/src/main/java/net/earthcomputer/clientcommands/features/EnchantmentCracker.java index 773f5a936..869dd2ac9 100644 --- a/src/main/java/net/earthcomputer/clientcommands/features/EnchantmentCracker.java +++ b/src/main/java/net/earthcomputer/clientcommands/features/EnchantmentCracker.java @@ -3,10 +3,10 @@ import com.mojang.logging.LogUtils; import net.earthcomputer.clientcommands.Configs; import net.earthcomputer.clientcommands.MultiVersionCompat; +import net.earthcomputer.clientcommands.task.ItemThrowTask; import net.earthcomputer.clientcommands.task.LongTask; import net.earthcomputer.clientcommands.task.LongTaskList; import net.earthcomputer.clientcommands.task.OneTickTask; -import net.earthcomputer.clientcommands.task.SimpleTask; import net.earthcomputer.clientcommands.task.TaskManager; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; @@ -23,7 +23,6 @@ import net.minecraft.util.RandomSource; import net.minecraft.util.StringRepresentable; import net.minecraft.world.inventory.EnchantmentMenu; -import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; @@ -373,33 +372,15 @@ public static ManipulateResult manipulateEnchantments(Item item, Predicate 0) { + taskList.addTask(new ItemThrowTask(timesNeeded, ItemThrowTask.FLAG_WAIT_FOR_ITEMS) { @Override public boolean condition() { if (Configs.playerCrackState != PlayerRandCracker.CrackState.MANIPULATING_ENCHANTMENTS) { taskList._break(); return false; } - - Slot slot = PlayerRandCracker.getBestItemThrowSlot(Minecraft.getInstance().player.containerMenu.slots); - //noinspection RedundantIfStatement - if (slot == null) { - return true; // keep waiting - } else { - return false; // ready to throw an item - } - } - - @Override - protected void onTick() { - } - - @Override - public void onCompleted() { - PlayerRandCracker.throwItem(); - scheduleDelay(); + return super.condition(); } }); } diff --git a/src/main/java/net/earthcomputer/clientcommands/features/PlayerRandCracker.java b/src/main/java/net/earthcomputer/clientcommands/features/PlayerRandCracker.java index 23d07f933..a84ced00b 100644 --- a/src/main/java/net/earthcomputer/clientcommands/features/PlayerRandCracker.java +++ b/src/main/java/net/earthcomputer/clientcommands/features/PlayerRandCracker.java @@ -362,6 +362,10 @@ public static boolean throwItem() { return true; } + public static void unthrowItem() { + seed = (seed * 0xdba6ed0471f1L + 0x25493d2c3b3cL) & MASK; + } + public static Slot getBestItemThrowSlot(List slots) { slots = slots.stream().filter(slot -> { if (!slot.hasItem()) { diff --git a/src/main/java/net/earthcomputer/clientcommands/features/SuggestionsHook.java b/src/main/java/net/earthcomputer/clientcommands/features/SuggestionsHook.java index 0b04c9398..98485bceb 100644 --- a/src/main/java/net/earthcomputer/clientcommands/features/SuggestionsHook.java +++ b/src/main/java/net/earthcomputer/clientcommands/features/SuggestionsHook.java @@ -18,6 +18,10 @@ private SuggestionsHook() { private static int currentSuggestionId = MAGIC_SUGGESTION_ID; private static final Int2ObjectMap> pendingSuggestions = new Int2ObjectOpenHashMap<>(); + public static CompletableFuture fence() { + return request("").thenAccept(suggestions -> {}); + } + public static CompletableFuture request(String command) { ClientPacketListener connection = Minecraft.getInstance().getConnection(); if (connection == null) { diff --git a/src/main/java/net/earthcomputer/clientcommands/mixin/MixinClientPacketListener.java b/src/main/java/net/earthcomputer/clientcommands/mixin/MixinClientPacketListener.java index 4e09ad9cc..95caa0c31 100644 --- a/src/main/java/net/earthcomputer/clientcommands/mixin/MixinClientPacketListener.java +++ b/src/main/java/net/earthcomputer/clientcommands/mixin/MixinClientPacketListener.java @@ -1,12 +1,12 @@ package net.earthcomputer.clientcommands.mixin; import com.mojang.brigadier.StringReader; -import net.cortex.clientAddon.cracker.SeedCracker; import net.earthcomputer.clientcommands.ClientcommandsDataQueryHandler; import net.earthcomputer.clientcommands.Configs; import net.earthcomputer.clientcommands.features.FishingCracker; import net.earthcomputer.clientcommands.features.PlayerRandCracker; import net.earthcomputer.clientcommands.features.SuggestionsHook; +import net.earthcomputer.clientcommands.task.ItemThrowTask; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl; import net.minecraft.client.multiplayer.ClientPacketListener; @@ -41,7 +41,9 @@ public void onHandleAddEntity(ClientboundAddEntityPacket packet, CallbackInfo ci return; } - SeedCracker.onEntityCreation(packet); + if (packet.getType() == EntityType.ITEM) { + ItemThrowTask.handleItemSpawn(packet); + } if (FishingCracker.canManipulateFishing()) { if (packet.getData() == player.getId() && packet.getType() == EntityType.FISHING_BOBBER) { diff --git a/src/main/java/net/earthcomputer/clientcommands/task/ItemThrowTask.java b/src/main/java/net/earthcomputer/clientcommands/task/ItemThrowTask.java new file mode 100644 index 000000000..958c2cf35 --- /dev/null +++ b/src/main/java/net/earthcomputer/clientcommands/task/ItemThrowTask.java @@ -0,0 +1,125 @@ +package net.earthcomputer.clientcommands.task; + +import com.mojang.logging.LogUtils; +import net.earthcomputer.clientcommands.Configs; +import net.earthcomputer.clientcommands.features.PlayerRandCracker; +import net.earthcomputer.clientcommands.features.SuggestionsHook; +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; +import org.slf4j.Logger; + +import java.lang.ref.WeakReference; +import java.util.Set; + +public class ItemThrowTask extends SimpleTask { + private static final Logger LOGGER = LogUtils.getLogger(); + + private static final Set MUTEX_KEYS = Set.of(ItemThrowTask.class); + + public static final int FLAG_URGENT = 1; + public static final int FLAG_WAIT_FOR_ITEMS = 2; + + private static WeakReference currentThrowTask = null; + + private final int totalItemsToThrow; + private final int flags; + + private int confirmedItemThrows; + private int sentItemThrows; + private float itemThrowsAllowedThisTick; + private boolean waitingFence = false; + private boolean failed = false; + + public ItemThrowTask(int itemsToThrow) { + this(itemsToThrow, 0); + } + + public ItemThrowTask(int itemsToThrow, int flags) { + this.totalItemsToThrow = itemsToThrow; + this.flags = flags; + } + + @Override + public boolean condition() { + return waitingFence || sentItemThrows != totalItemsToThrow || sentItemThrows > confirmedItemThrows; + } + + @Override + protected void onTick() { + itemThrowsAllowedThisTick += Configs.itemThrowsPerTick; + + while (((flags & FLAG_URGENT) != 0 || itemThrowsAllowedThisTick >= 1) && sentItemThrows < totalItemsToThrow) { + itemThrowsAllowedThisTick--; + if (!PlayerRandCracker.throwItem()) { + if ((flags & FLAG_WAIT_FOR_ITEMS) != 0) { + return; + } + failed = true; + _break(); + onFailedToThrowItem(); + return; + } + sentItemThrows++; + } + + if (!waitingFence && sentItemThrows == totalItemsToThrow && confirmedItemThrows < sentItemThrows) { + waitingFence = true; + SuggestionsHook.fence().thenAccept(v -> { + if (sentItemThrows > confirmedItemThrows) { + LOGGER.info("Server rejected {} item throws. Rethrowing them.", sentItemThrows - confirmedItemThrows); + while (sentItemThrows > confirmedItemThrows) { + PlayerRandCracker.unthrowItem(); + sentItemThrows--; + } + } + waitingFence = false; + }); + } + } + + @Override + public void initialize() { + currentThrowTask = new WeakReference<>(this); + } + + @Override + public void onCompleted() { + if (!failed) { + onSuccess(); + } + currentThrowTask = null; + } + + @Override + public Set getMutexKeys() { + return MUTEX_KEYS; + } + + protected void onFailedToThrowItem() { + } + + protected void onSuccess() { + } + + protected void onItemSpawn(ClientboundAddEntityPacket packet) { + } + + public static void handleItemSpawn(ClientboundAddEntityPacket packet) { + ItemThrowTask task = currentThrowTask == null ? null : currentThrowTask.get(); + if (task == null) { + return; + } + + LocalPlayer player = Minecraft.getInstance().player; + if (player == null) { + return; + } + if (player.getEyePosition().distanceToSqr(packet.getX(), packet.getY(), packet.getZ()) > 1) { + return; + } + + task.confirmedItemThrows++; + task.onItemSpawn(packet); + } +} diff --git a/src/main/java/net/earthcomputer/clientcommands/task/LongTask.java b/src/main/java/net/earthcomputer/clientcommands/task/LongTask.java index 18f3116de..8cafcca00 100644 --- a/src/main/java/net/earthcomputer/clientcommands/task/LongTask.java +++ b/src/main/java/net/earthcomputer/clientcommands/task/LongTask.java @@ -1,10 +1,13 @@ package net.earthcomputer.clientcommands.task; +import java.util.Set; + /** * Acts like a for loop that can delay between iterations to allow the game to continue ticking */ public abstract class LongTask { + boolean isInitialized = false; private boolean delayScheduled; private boolean broken = false; @@ -42,4 +45,7 @@ public boolean stopOnLevelUnload(boolean isDisconnect) { return true; } + public Set getMutexKeys() { + return Set.of(); + } } diff --git a/src/main/java/net/earthcomputer/clientcommands/task/TaskManager.java b/src/main/java/net/earthcomputer/clientcommands/task/TaskManager.java index 351e68d78..b7bcf8f9d 100644 --- a/src/main/java/net/earthcomputer/clientcommands/task/TaskManager.java +++ b/src/main/java/net/earthcomputer/clientcommands/task/TaskManager.java @@ -3,39 +3,52 @@ import net.earthcomputer.clientcommands.features.Relogger; import java.util.ArrayList; -import java.util.Iterator; +import java.util.HashSet; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; +import java.util.Set; public class TaskManager { - private static final List newTasks = new ArrayList<>(); private static final Map tasks = new LinkedHashMap<>(); private static long nextTaskId = 1; + private static String forceAddedTaskName = null; public static void tick() { - newTasks.forEach(LongTask::initialize); - newTasks.clear(); - if (tasks.isEmpty()) { return; } + Set mutexKeys = new HashSet<>(); + var iteratingTasks = new ArrayList<>(tasks.entrySet()); while (!iteratingTasks.isEmpty()) { var itr = iteratingTasks.iterator(); while (itr.hasNext()) { var taskEntry = itr.next(); LongTask task = taskEntry.getValue(); + Set taskMutexKeys = task.getMutexKeys(); + if (mutexKeys.stream().anyMatch(taskMutexKeys::contains)) { + continue; + } + mutexKeys.addAll(taskMutexKeys); + if (!task.isInitialized) { + task.initialize(); + task.isInitialized = true; + } if (task.isCompleted()) { + forceAddedTaskName = null; task.onCompleted(); - tasks.remove(taskEntry.getKey()); + if (!taskEntry.getKey().equals(forceAddedTaskName)) { + tasks.remove(taskEntry.getKey()); + } itr.remove(); + mutexKeys.removeAll(taskMutexKeys); } else { task.body(); - if (!task.isCompleted()) + if (!task.isCompleted()) { task.increment(); + } if (task.isDelayScheduled()) { task.unscheduleDelay(); itr.remove(); @@ -57,24 +70,12 @@ public static void onLevelUnload(boolean isDisconnect) { } } } - List oldNewTasks = new ArrayList<>(); - { - Iterator itr = newTasks.iterator(); - while (itr.hasNext()) { - LongTask newTask = itr.next(); - if (newTask.stopOnLevelUnload(isDisconnect)) { - itr.remove(); - oldNewTasks.add(newTask); - } - } - } if (isDisconnect && Relogger.isRelogging) { Relogger.relogSuccessTasks.add(() -> { for (var oldTask : oldTasks) { tasks.put(oldTask.getKey(), oldTask.getValue()); } - newTasks.addAll(oldNewTasks); }); } } @@ -82,10 +83,14 @@ public static void onLevelUnload(boolean isDisconnect) { public static String addTask(String name, LongTask task) { String actualName = (nextTaskId++) + "." + name; tasks.put(actualName, task); - newTasks.add(task); return actualName; } + public static void forceAddTask(String fullName, LongTask task) { + tasks.put(fullName, task); + forceAddedTaskName = fullName; + } + public static int getTaskCount() { return tasks.size(); }