Asynchronous entity world add

Discussion in 'Spigot Plugin Development' started by thienbao860, Nov 5, 2019.

  1. Code (Java):
        @EventHandler
        public void spawnItem(ItemSpawnEvent event) {
            Chunk chunk = event.getLocation().getChunk();
            Item item = event.getEntity();

            if (event.getEntity() == null) return;
            Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> scan(chunk, item));

        }

        private void scan(Chunk chunk, @NotNull Item item) {

            ItemStack itemStack = item.getItemStack();
            for (GInventory gInv : api.getGInventories().toArray(new GInventory[0])) {
                if (Arrays.asList(chunk.getTileEntities()).contains(gInv.getLocation().getBlock().getState())) {
     
               //stuffs
                }
            }
     
    I need to make it so that when item spawn, the scan method will start searching for the chunk that available item is at, and if one block of that chunk has "special item" then item will be transported there.
    But because if I don't add scan method on async, the server will be laggy as hell when trying to scan 30+ stacks dropping out at the same time, so i'm making the async on it. And if i make it async, the error "async entity world add" will present. I checked the log, and it's from this line of code:
    Code (Text):
    if (Arrays.asList(chunk.getTileEntities()).contains(gInv.getLocation().getBlock().getState())) {
    . Any fix can be done?
     
    #1 thienbao860, Nov 5, 2019
    Last edited: Nov 5, 2019
  2. That is hell of a confusing "error description" and way of asking for help. I actually had to read it 3 times to even get an idea of what you are trying to ask here.

    Please rephrase what you are trying to say, because I (and probably nobody else here either) understands what you want.

    Some guidelines for asking a question with some structure:
    What are you trying to do (in detail)?
    What is stopping you from doing so (error)?
    What have you tried to fix the issue?
    What are you asking for / what is your question?
    Add full code of specific parts for clarification and mention what lines (by quoting them) are important for the issue.

    Thanks.
     
  3. I edited the thread. Hope that will help you understand
     
  4. You should not be using Bukkit API in Async threads
    (That's the error, the server won't allow operations like World creating, chunks, blocks, etc to be done async)
     
  5. Do not use bukkit api asynchronously, only your own information wrappers that do not touch on the bukkit api should be used offthread (and bukkit api that explicitly states that it is safe to be used offthread, iE chunk snapshots.)
     
  6. To get this straight:

    You are spawning an item in yourself or is the item spawning on itself?
    Then you are going ahead and loop through every TileEntity that is inside the chunk the item was spawned in to see if any of those TileEntities matches one of those "special GInventory API BlockStates" (whatever the hell those are) and if that is the case, you do some stuff ... or as you described you move that item to that block?
    Also you do this on every item spawn event.

    This in general sounds like a very bad idea and / or a very ineffective way of doing this. To be honest I haven't put much thought into this, but even by only seeing your "problem" and how you are trying so "solve" it I can tell there must be a better way.

    Other than that, coming to your real problem here:

    As soon as you go into any form of asynchronous task / thread you will be unable to access any live data (we just call it that for now) from the main server thread. Speaking: Blocks, Players, Items or any other information that is synchronized between server and clients. Because that's the whole reason: it is synchronized and doing an async- or UN-synchronized access on it is just not allowed.
    What you can do is take data from the server, kind of like a snapshot of the exact moment you take it, throw that into asynchronous tasks / threads and then after you have your result or did your computing on it. you can go back and apply those changes or whatever you want to do on the server normally - but on the main server thread, not out of the asynchronous environment, which means you need to get it back to a synchronous state first.

    I hope this helps you, if not say so.
     
    • Like Like x 1
  7. Code (Java):
        @EventHandler
        public void spawnItem(ItemSpawnEvent event) {
            Chunk chunk = event.getLocation().getChunk();
            Item item = event.getEntity();
            if (event.getEntity() == null) return;

            scan(chunk, item);
        }

        private void scan(Chunk chunk, Item item) {

            ItemStack itemStack = item.getItemStack();
            if (isItemModified(itemStack) || isItemBlackListed(itemStack) || isItemEnchanted(itemStack)) {
                return;
            }
            for (GInventory gInv : api.getGInventories().toArray(new GInventory[0])) {
                if (Arrays.asList(chunk.getTileEntities()).contains(gInv.getLocation().getBlock().getState())) {
                    Bukkit.getScheduler().runTaskLaterAsynchronously(this.plugin, () -> {
       //stuff
    }
                 
                }
    break;
            }
    }
    That is my current code after thinking for a while. However this is what I get in the timing log:
    Code (Text):
        Line 188:     Plugin: GucciChest v1.5 Event: me.thienbao860.plugin.gchest.listeners.ChestListener::spawnItem(ItemSpawnEvent) Time: 515241535 Count: 121 Avg: 4258194 Violations: 8
     
  8. md_5

    Administrator Developer

    You should cache the scan result and also make sure that its fast as possible
     
  9. I'm sorry for being a noob cause I'm a starter, but how do you mean? I mean, what's the point of caching and how it will be used?
     
    #9 thienbao860, Nov 7, 2019
    Last edited: Nov 7, 2019
  10. If you can grasp the term concurrency and the API for it in the java libraries
    You can make use a FUTURE and run the scan in a separate thread then check the result every tick.
    (Can get complicated real quick)
    or you can use 1 async bukkit runnable to run the scan and if you need to spawn the item
    you can then create 1 sync bukkit runnable inside the async runnable.

    Cache the tile entities then release the chunks so you do not lock the main thread

    Cache = saving values to a storage for you to get later example of this is any collection/
    Set/List/Vector/Map etc. Common cache used is memory for fast access.

    You might also put a check if the detected tile entity is still in the main thread world and if
    the chunk the tile entity is on is still loaded before spawning the item sync.

    There's no need to apologize for being a starter we all started there. Just push through slowly.
    Practice/Write every code 10x or more and read documentations until you can write code by
    instinct.