Reputation: 1
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