Solved Asynchronous getEntities()

Discussion in 'Spigot Plugin Development' started by MiniGutt, Jul 6, 2021.

  1. Hey!

    I have stumbled upon a problem now after the 1.17 update. What I am trying to do is getting the entities in a world by using world.getEntites() or world.getNearbyEntities(BoundingBox), but also do this asynchronous if possible, which is was before. I have read something about this not being possible anymore, so I tried to run just the getEntities synchronous, but cant figure out just how.

    Code (Text):
        Bukkit.getScheduler().runTaskAsynchronously(Plugin.instance, () -> {

            try {
                World world = p.getWorld();
                List<Entity> entities = new ArrayList<Entity>();
                for (Entity e : world.getEntities()) { ** Line where I get the error, shown as line 98 in the error **
                    if (e instanceof Hanging || e instanceof ArmorStand || e instanceof Villager || e instanceof StorageMinecart)
                        entities.add(e);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }

        });
    Code (Text):
    [22:22:15 WARN]: java.lang.IllegalStateException: Asynchronous getNearbyEntities!
    [22:22:15 WARN]:        at org.spigotmc.AsyncCatcher.catchOp(AsyncCatcher.java:15)
    [22:22:15 WARN]:        at org.bukkit.craftbukkit.v1_17_R1.CraftWorld.getNearbyEntities(CraftWorld.java:1273)
    [22:22:15 WARN]:        at org.bukkit.craftbukkit.v1_17_R1.CraftWorld.getNearbyEntities(CraftWorld.java:1268)
    [22:22:15 WARN]:        at no.spillere.protection.handlers.WorldEditHandler.getEntitiesInSelection(WorldEditHandler.java:183)
    [22:22:15 WARN]:        at no.spillere.protection.handlers.WorldEditHandler.lambda$protectSelection$0(WorldEditHandler.java:98)
    [22:22:15 WARN]:        at org.bukkit.craftbukkit.v1_17_R1.scheduler.CraftTask.run(CraftTask.java:100)
    [22:22:15 WARN]:        at org.bukkit.craftbukkit.v1_17_R1.scheduler.CraftAsyncTask.run(CraftAsyncTask.java:54)
    [22:22:15 WARN]:        at com.destroystokyo.paper.ServerSchedulerReportingWrapper.run(ServerSchedulerReportingWrapper.java:22)
    [22:22:15 WARN]:        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
    [22:22:15 WARN]:        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
    [22:22:15 WARN]:        at java.base/java.lang.Thread.run(Thread.java:831)
    Dont mind the error saying getNearbyEntities, as this is from when I tried world.getNearbyEntities(BoundingBox). getEntities produced basically the exact same error.

    I have also tried to make a method that returns the entities in the world, but that doesn't seem to make it run synchronous.
     
  2. SteelPhoenix

    Moderator

    You can use BukkitScheduler#callSyncMethod(Plugin, Callable<T>) to call a method sync from async context and use its result.
    Why do you need to run this code async though?
     
  3. Further down in the try I am adding the entities to a database. I don't know if this helps, but I don't want the server to stop if something times out or something, since if I remember right the server waits until it is done if it is run sync, right?
     
  4. You shouldn't run this code async. That's explicitly what the stack trace is telling you:
    This is what is preventing you:
    Code (Text):

    public class AsyncCatcher {
        public static boolean enabled = true;

        public AsyncCatcher() {
        }

        public static void catchOp(String reason) {
            if(enabled && Thread.currentThread() != MinecraftServer.getServer().primaryThread) {
                throw new IllegalStateException("Asynchronous " + reason + "!");
            }
        }
    }
     
  5. Get the List<Entity> with your world.getNearbyEntities() method in a synchronous context, and then start an async task to do the database process.
     
  6. You must not use Bukkit API asynchronously.