Gicjgc Izcizcizc
Gicjgc Izcizcizc

Reputation: 1

Minecraft Mod Error: Accessing LegacyRandomSource from multiple threads

Description of issue: Accessing LegacyRandomSource from multiple threads

java.lang.IllegalStateException: Accessing LegacyRandomSource from multiple threads
   at net.minecraft.class_5798.method_33564(class_5798.java:84)
   at net.minecraft.class_5820.method_43156(class_5820.java:49)
   at net

I'm working on this Minecraft mod that runs on a multiplayer server. The mod is designed to automate item collection from a spawner (there is a spawner plugin on the server). The main tasks in the mod, boneAction and arrowAction, involve sequences of actions that need to be executed with delays.

Originally, I wanted the methods (boneAction, arrowAction and startMacroSequence) to run sequentially, ensuring that the following method starts only after the previous one has completely finished, with an additional wait of 1-2 seconds but I don't think that is possible

My Code:

public class PeacefulMacroClient implements ClientModInitializer {

    private static KeyBinding startKeyBind;
    private static KeyBinding stopKeyBind;
    private ScheduledExecutorService scheduler;
    private BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
    private volatile boolean macroRunning = false;
    private final int bone_delay = 300;
    private final Object lock = new Object();

    @Override
    public void onInitializeClient() {
        startKeyBind = KeyBindingHelper.registerKeyBinding(new KeyBinding(
                "key.startMacro",
                InputUtil.Type.KEYSYM,
                GLFW.GLFW_KEY_L,
                "category.peacefulmacro"
        ));

        stopKeyBind = KeyBindingHelper.registerKeyBinding(new KeyBinding(
                "key.stopMacro",
                InputUtil.Type.KEYSYM,
                GLFW.GLFW_KEY_APOSTROPHE,
                "category.peacefulmacro"
        ));

        scheduler = Executors.newScheduledThreadPool(1);
        ClientTickEvents.END_CLIENT_TICK.register(this::onClientTick);
    }

    private void onClientTick(MinecraftClient client) {
        ClientPlayerEntity player = client.player;

        if (player != null) {
            if (startKeyBind.wasPressed() && !macroRunning) {
                startMacroSequence(player);
            }
            if (stopKeyBind.wasPressed()) {
                stopMacro(player);
            }
        }
    }

    private void startMacroSequence(ClientPlayerEntity player) {
        taskQueue.clear();
        macroRunning = true;

        int initialDelay = 1000;

        scheduleTask(() -> {
            BlockHitResult spawnerHit = checkForSpawner(player);
            rightClickSpawner(player, spawnerHit);
        }, initialDelay);

        scheduleTask(() -> openSpawnerStorage(player), initialDelay + 1000);

        scheduleTask(() -> {
            if (hasArrows(player)) {
                arrowAction(player);
            } else if (hasBones(player)) {
                boneAction(player);
            } else {
                stopMacro(player);
            }
        }, initialDelay + 2000);
    }

    private void boneAction(ClientPlayerEntity player) {
        int delayIncrement = 1000;

        scheduleTask(() -> {
            player.sendMessage(Text.literal("Debug: getBones"));
            getBones(player);
        }, delayIncrement);
        scheduleTask(() -> {
            player.sendMessage(Text.literal("Debug: closeInventory"));
            closeInventory(player);
        }, 3 * delayIncrement);
        scheduleTask(() -> {
            player.sendMessage(Text.literal("Debug: openPlayerInventory"));
            openPlayerInventory(player);
        }, 4 * delayIncrement);

        scheduleTask(() -> {
            player.sendMessage(Text.literal("Debug: craftBoneMeal"));

            int boneStartIndex = 33;
            int boneEndIndex = 44;
            final int targetSlot = 1;
            final int out = 0;
            int cumulativeDelay = 0;

            for (int i = boneStartIndex; i <= boneEndIndex; i++) {
                int slotIndex = i;
                cumulativeDelay += bone_delay;

                scheduleTask(() -> {
                    synchronized (lock) {
                        clickSlot(player, slotIndex);
                        clickSlot(player, targetSlot);
                        shiftClickItem(player, out);
                    }
                }, cumulativeDelay);
            }
        }, 6 * delayIncrement);

        scheduleTask(() -> {
            player.sendMessage(Text.literal("Debug: dropBonesSlowly"));

            int cumulativeDelay = 0;
            for (int i = 9; i <= 44; i++) {
                int slotIndex = i;
                cumulativeDelay += bone_delay;

                scheduleTask(() -> {
                    synchronized (lock) {
                        ItemStack stack = player.currentScreenHandler.getSlot(slotIndex).getStack();
                        if (stack.getItem() == Items.BONE_MEAL) {
                            dropItem(player, slotIndex);
                        }
                    }
                }, cumulativeDelay);
            }
        }, 12 * delayIncrement);

        scheduleTask(() -> {
            closeInventory(player);
        }, 24 * delayIncrement);
        scheduleTask(() -> {
            player.sendMessage(Text.literal("Debug: Restarting macro sequence"));
            startMacroSequence(player);
        }, 25 * delayIncrement);
    }

    private void arrowAction(ClientPlayerEntity player) {
        player.sendMessage(Text.literal("Debug: Starting arrowAction"));
        int delayIncrement = 1000;

        scheduleTask(() -> {
            player.sendMessage(Text.literal("Debug: collectArrows"));
            collectArrows(player);
        }, delayIncrement);
        scheduleTask(() -> {
            player.sendMessage(Text.literal("Debug: closeInventory"));
            closeInventory(player);
        }, 2 * delayIncrement);
        scheduleTask(() -> {
            player.sendMessage(Text.literal("Debug: executeCommand sell"));
            executeCommand(player, "sell");
        }, 3 * delayIncrement);
        scheduleTask(() -> {
            player.sendMessage(Text.literal("Debug: sellArrows"));
            sellArrows(player);
        }, 4 * delayIncrement);
        scheduleTask(() -> {
            player.sendMessage(Text.literal("Debug: closeInventory again"));
            closeInventory(player);
        }, 5 * delayIncrement);
        scheduleTask(() -> {
            player.sendMessage(Text.literal("Debug: Restarting macro sequence"));
            startMacroSequence(player);
        }, 6 * delayIncrement);
    }

    private void scheduleTask(Runnable task, int delay) {
        scheduler.schedule(() -> {
            if (macroRunning) {
                try {
                    taskQueue.put(task);
                    processTaskQueue();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }, delay, TimeUnit.MILLISECONDS);
    }

    private void processTaskQueue() {
        scheduler.submit(() -> {
            while (macroRunning && !taskQueue.isEmpty()) {
                try {
                    Runnable task = taskQueue.take();
                    synchronized (lock) {
                        task.run();
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        });
    }

    private BlockHitResult checkForSpawner(ClientPlayerEntity player) {
        BlockHitResult hitResult = (BlockHitResult) player.raycast(5.0D, 0.0F, false);
        if (hitResult.getType() == HitResult.Type.BLOCK) {
            if (player.getWorld().getBlockState(hitResult.getBlockPos()).getBlock() == Blocks.SPAWNER) {
                player.sendMessage(Text.of("This block is a spawner"));
                return hitResult;
            }
        }
        player.sendMessage(Text.literal("This block is not a spawner"));
        stopMacro(player);
        return null;
    }

    private void rightClickSpawner(ClientPlayerEntity player, BlockHitResult hitResult) {
        MinecraftClient client = MinecraftClient.getInstance();
        if (client.interactionManager != null) {
            client.interactionManager.interactBlock(player, Hand.MAIN_HAND, hitResult);
        }
    }

    private void openSpawnerStorage(ClientPlayerEntity player) {
        MinecraftClient client = MinecraftClient.getInstance();
        if (client.interactionManager != null) {
            client.interactionManager.clickSlot(player.currentScreenHandler.syncId, 11, 0, SlotActionType.PICKUP, player);
            player.sendMessage(Text.literal("Opened spawner storage"));
        }
    }

    private boolean hasArrows(ClientPlayerEntity player) {
        ScreenHandler handler = player.currentScreenHandler;
        boolean arrowsFound = false;

        // Überprüfe Slots 0-35 des offenen Inventars
        for (int i = 0; i < 36; i++) {
            ItemStack stack = handler.getSlot(i).getStack();
            if (stack.getItem() == Items.ARROW) {
                arrowsFound = true;
                break;
            }
        }

        player.sendMessage(Text.literal("Arrows found: " + arrowsFound));
        return arrowsFound;
    }

    private boolean hasBones(ClientPlayerEntity player) {
        ScreenHandler handler = player.currentScreenHandler;
        boolean bonesFound = false;

        // Überprüfe Slots 0-35 des offenen Inventars
        for (int i = 0; i < 36; i++) {
            ItemStack stack = handler.getSlot(i).getStack();
            if (stack.getItem() == Items.BONE) {
                bonesFound = true;
                break;
            }
        }

        player.sendMessage(Text.literal("Bones found: " + bonesFound));
        return bonesFound;
    }

    private void getBones(ClientPlayerEntity player) {
        if (player.currentScreenHandler instanceof GenericContainerScreenHandler) {
            GenericContainerScreenHandler container = (GenericContainerScreenHandler) player.currentScreenHandler;
            for (int i = 0; i < container.slots.size(); i++) {
                ItemStack stack = container.getSlot(i).getStack();
                if (stack.getItem() == Items.BONE) {
                    // Aktionen zum Aufnehmen der Knochen
                    clickSlot(player, i);
                    break;
                }
            }
        }
    }

    private void openPlayerInventory(ClientPlayerEntity player) {
        player.closeHandledScreen();
        player.openInventory(player.getInventory());
    }

    private void clickSlot(ClientPlayerEntity player, int slotIndex) {
        MinecraftClient client = MinecraftClient.getInstance();
        if (client.interactionManager != null) {
            client.interactionManager.clickSlot(player.currentScreenHandler.syncId, slotIndex, 0, SlotActionType.PICKUP, player);
        }
    }

    private void shiftClickItem(ClientPlayerEntity player, int slotIndex) {
        MinecraftClient client = MinecraftClient.getInstance();
        if (client.interactionManager != null) {
            client.interactionManager.clickSlot(player.currentScreenHandler.syncId, slotIndex, 0, SlotActionType.QUICK_MOVE, player);
        }
    }

    private void dropItem(ClientPlayerEntity player, int slotIndex) {
        MinecraftClient client = MinecraftClient.getInstance();
        if (client.interactionManager != null) {
            client.interactionManager.clickSlot(player.currentScreenHandler.syncId, slotIndex, 1, SlotActionType.THROW, player);
        }
    }

    private void sellArrows(ClientPlayerEntity player) {
        if (player.currentScreenHandler instanceof MerchantScreenHandler) {
            MerchantScreenHandler merchantHandler = (MerchantScreenHandler) player.currentScreenHandler;
            for (int i = 0; i < merchantHandler.getRecipes().size(); i++) {
                MerchantRecipe recipe = merchantHandler.getRecipes().get(i);
                if (recipe.getSellItem().getItem() == Items.EMERALD) {
                    clickSlot(player, i);
                    break;
                }
            }
        }
    }

    private void collectArrows(ClientPlayerEntity player) {
        if (player.currentScreenHandler instanceof GenericContainerScreenHandler) {
            GenericContainerScreenHandler container = (GenericContainerScreenHandler) player.currentScreenHandler;
            for (int i = 0; i < container.slots.size(); i++) {
                ItemStack stack = container.getSlot(i).getStack();
                if (stack.getItem() == Items.ARROW) {
                    clickSlot(player, i);
                }
            }
        }
    }

    private void executeCommand(ClientPlayerEntity player, String command) {
        player.networkHandler.sendChatCommand(command);
    }

    private void closeInventory(ClientPlayerEntity player) {
        player.closeHandledScreen();
    }

    private void stopMacro(ClientPlayerEntity player) {
        macroRunning = false;
        taskQueue.clear();
        player.sendMessage(Text.literal("Macro stopped"));
    }
}

Upvotes: 0

Views: 339

Answers (0)

Related Questions