1.17.x Search entire world for entities

Discussion in 'Spigot Plugin Development' started by MrMel, Jul 19, 2021.

  1. Hello there!

    Now I know searching the entire world for something is slow and laggy. But anyway, what i'm needing is something that can search all chest minecarts (that's been loaded) for a specific item. I don't really care how slow it is. I need to find a specific item in a world. I have one way which I could do that, just a simple for loop. But I'm looking for a better way, and preferably as lag friendly as possible.

    So, what is the best way to do this?
    Thanks.
     
  2. Code (Java):
    yourWorld.getEntitiesByClass(StorageMinecart.class).getInventory();
    Then search the inventory.
     
  3. This will not work. getEntitiesByClass(..) returns a collection, which you need to loop over. This collection itself does not have an inventory, but the elements in the loop will.
     
  4. I guess something like this might work (yes, finally a usage of flatmap :ROFLMAO:)
    Code (Java):
                player.getWorld().getEntitiesByClasses(InventoryHolder.class).stream()
                        .map(InventoryHolder.class::cast)
                        .flatMap(inventoryHolder -> Stream.of(inventoryHolder.getInventory().getContents()))
                        .forEach(itemStack -> {
                            //filter each itemstack
                        });
     
    • Like Like x 1
    • Creative Creative x 1
  5. You might want to switch the class to minecarts as this will also include all chests I believe etc?
     
  6. Of course it's a collection... I am guessing OP knows something about coding so the point was to show him which functions to use.
    Loop over them and you're done.
     
  7. I think regular minecarts don't have inventories, only specific ones such as StorageMinecart or HopperMinecart.
    I honestly just assumed that everything that has an inventory implements InventoryHolder in some way, because that's the only thing which would make sense to me.
     
  8. #getEntitiesByClass only accepts a class that extends from Entity. Chest is a tile entity, and tile entities do not extend from Entity, meaning they cannot be gotten through this method. Naturally, that also means that you can't just pass InventoryHolder.class because it does not extend from Entity; if you could though, you'd also be getting every other entity that supports an inventory: horses, zombies, skeletons, villagers, etc.
     
  9. You're correct - everything that has any sort of inventory is an extension of InventoryHolder in some way or another.
    But in OP's case he needs to use StorageMinecart in #getEntitiesByClass and not just Minecart
     
  10. If you look carefully you see that I used getEntitiesByClasses() which uses an unbound wildcard instead of the other methods which all bound to Entity. Either I'm missing something with the wildcards or this method should actually work with any interface (but may return an empty collection if an interface/class is given, which is not related to any entity ever)
     
  11. Carefully read the name of the function: getEntitiesByClasses.
     
  12. Instead of guessing stuff based on the method name and documentation I went ahead and tested my code. It does work. To me this makes sense, because some entities implement InventoryHolder and therefore an instanceof check will succeed in these cases.
     
  13. Do you mean that this variation accepts classes that don't necessarily extend the Entity class?
     
  14. Yes, they would have made the method signature
    Code (Java):
    public Collection<? extends Entity> getEntitiesByClasses(Class<? extends Entity>... classes) {
    that way if they wanted to force you staying within Entity.
     
    • Informative Informative x 1
  15. In that case thx! That's very useful information!
     
  16. I just realized that I read the question wrong...
    Of course you're right.
    Code (Java):
            world.getEntitiesByClass(StorageMinecart.class).stream()
                    .flatMap(storageMinecart -> Stream.of(storageMinecart.getInventory().getContents()))
                    .forEach(itemStack -> {
                     
                    });
    Or if you want to make to include every minecart with an inventory (e.g. HopperMinecart) and make it future-safe this would work
    Code (Java):
            world.getEntitiesByClass(Minecart.class).stream()
                    .filter(InventoryHolder.class::isInstance)
                    .map(InventoryHolder.class::cast)
                    .flatMap(inventoryHolder -> Stream.of(inventoryHolder.getInventory().getContents()))
                    .forEach(itemStack -> {

                    });
    Lastly, if you go full maniac/functional we can maximize method referencing (for no reason lol)
    Code (Java):
            world.getEntitiesByClass(Minecart.class).stream()
                    .filter(InventoryHolder.class::isInstance)
                    .map(InventoryHolder.class::cast)
                    .map(InventoryHolder::getInventory)
                    .map(Inventory::getContents)
                    .flatMap(Stream::of)
                    .forEach(itemStack -> {
                     
                    });
     
    #16 wand555, Jul 19, 2021
    Last edited: Jul 19, 2021
    • Friendly Friendly x 1
  17. Indeed, I did miss that. Interestingly that should work fine, because it only checks that one of provided classes is assignable from the entity's class. I'll stand by the idea that OP should search by StorageMinecart.class[icode] specifically, instead of a blanket interface like InventoryHolder.