Solved Change Villager Trades

Discussion in 'Spigot Plugin Development' started by KleeSup, Feb 4, 2020.

  1. Hi Guys!

    Im currently working on an Plugin which checks the MerchantRecipes from an Clicked Villager. If the Recipe is an Enchanted Book with Silk Touch or Mending the Recipes will be removed an the villager trade will open up without the two Recipes. But it doesn't seems to work...

    Code (Java):
    @EventHandler
        public void onVillagerInteract(final PlayerInteractAtEntityEvent e){
            final Player p = e.getPlayer();
            if(!(e.getRightClicked() instanceof Villager))return;
            final Villager villager = (Villager)e.getRightClicked();

            final List<MerchantRecipe> recipes = villager.getRecipes();
            for(MerchantRecipe recipe : recipes){
                if(!recipe.getResult().getType().equals(Material.ENCHANTED_BOOK))continue;
                if(p.getName().equalsIgnoreCase("KleeSup"))p.sendMessage("InteractTrade-DEBUG3");
                final EnchantmentStorageMeta im = (EnchantmentStorageMeta)recipe.getResult().getItemMeta();
                if(im.hasStoredEnchant(Enchantment.SILK_TOUCH)||im.hasStoredEnchant(Enchantment.MENDING)){
                    if(p.getName().equalsIgnoreCase("KleeSup"))p.sendMessage("InteractTrade-DEBUG4");
                    recipes.remove(recipe);
                }
            }
            villager.setRecipes(recipes);
            if(recipes.size() < 1) {
                e.setCancelled(true);
            }

        }
    The EventHandler is registered and all Debug-Messages will be sent but the recipes will not be removed from my villager

    I hope u can help me Guys! (Sorry for bad English)
     
    #1 KleeSup, Feb 4, 2020
    Last edited: Feb 4, 2020
  2. This is a bad setup for a loop. Anytime you want to edit a loop you are looping through you need to create a duplicate loop.
    Furthermore, the villager.getRecipes() method return an unmodifiable loop, so I would assume you are getting console spam anytime you are rightclicking said villager.

    There were a few things wrong with your code so I decided to explain them in the code.

    Code (Java):
        @EventHandler public void onInteract(PlayerInteractAtEntityEvent e) {
            if (!(e.getRightClicked() instanceof Villager)) return;

            Villager villager = (Villager) e.getRightClicked();

            /* This uses google guava (which spigot includes) */
            /* google guava, just takes the elements from the list and puts it into a new list (that we can modify) */
            List<MerchantRecipe> recipes = Lists.newArrayList(villager.getRecipes());

            /* Convert the list to an iterator so we can safely remove values from it while looping through the iterator */
            Iterator<MerchantRecipe> recipeIterator;
            for (recipeIterator = recipes.iterator(); recipeIterator.hasNext(); ) {
                MerchantRecipe recipe = recipeIterator.next();

                if (recipe.getResult().getType().equals(Material.ENCHANTED_BOOK)) {
                    /* No reason to make this final, please try to avoid adding random modifiers to your variables */
                    EnchantmentStorageMeta meta = (EnchantmentStorageMeta) recipe.getResult().getItemMeta();

                    if (meta.hasStoredEnchant(Enchantment.SILK_TOUCH) || meta.hasStoredEnchant(Enchantment.MENDING)) {
                        recipeIterator.remove();
                    }
                }
            }

            villager.setRecipes(recipes);

            if (villager.getRecipes().size() == 0) {
                e.setCancelled(true);
            }
        }
     
    • Agree Agree x 1
  3. Ok I will check this out. Why I cant use List<Type> list = class.getListXYZ(); ? And final isn't an random modifier. Ive learned final is good to use when u know that u wont change that variable in your code again.
     
  4. 1) In this specific instance, villager.getRecipes() returns a list you can't edit. That's why you can't use it in this instance.

    2) It's really programming style, but it just makes you lose time typing out final every time when you really don't need to do it.
     
  5. to 1) how can I know that the instance is not changeable?
    and to 2) Ive learned that it saves a little little bit of memory so I use it

    So now the Villager-GUI wont show up. Before I reloaded the System the Villager had 10 or more Trades. 1 of them was the trade with the book. So now the Part with if (villager.getRecipes().size() == 0) will be executed but without sense.
     
  6. I just spawned a new Villager and when i rightclick the villager he will not open and all his recipes are removed.
     
  7. 1) You look at the API javadocs (https://hub.spigotmc.org/javadocs/spigot/overview-summary.html)
    2) Again, it's up to you.

    Keep in mind, that changes you make to the recipe list stay on the villager, so if you remove any values they go away permanently. You might be better off running this inside of EntitySpawnEvent and checking if the entity is a villager, then running your code.
     
  8. No, I mean that the event removes ALL recipes, not just those where the result is an Enchanted Book.
     
  9. Doesn't seem to be happening for, just ran it on my server.
     
  10. Ok I resent the code but except the Debug-Message nothing changed

    Code (Text):
    @EventHandler
        public void onVillagerInteract(final PlayerInteractAtEntityEvent e){
            final Player p = e.getPlayer();
            if (!(e.getRightClicked() instanceof Villager)) return;

            Villager villager = (Villager) e.getRightClicked();

            /* This uses google guava (which spigot includes) */
            /* google guava, just takes the elements from the list and puts it into a new list (that we can modify) */
            List<MerchantRecipe> recipes = Lists.newArrayList(villager.getRecipes());

            /* Convert the list to an iterator so we can safely remove values from it while looping through the iterator */
            Iterator<MerchantRecipe> recipeIterator;
            for (recipeIterator = recipes.iterator(); recipeIterator.hasNext(); ) {
                MerchantRecipe recipe = recipeIterator.next();

                if (recipe.getResult().getType().equals(Material.ENCHANTED_BOOK)) {
                    /* No reason to make this final, please try to avoid adding random modifiers to your variables */
                    EnchantmentStorageMeta meta = (EnchantmentStorageMeta) recipe.getResult().getItemMeta();

                    if (meta.hasStoredEnchant(Enchantment.SILK_TOUCH) || meta.hasStoredEnchant(Enchantment.MENDING)) {
                        recipeIterator.remove();
                    }
                }
            }

            villager.setRecipes(recipes);

            if (villager.getRecipes().size() == 0) {
                p.sendMessage("CLOSED");
                //e.setCancelled(true);
            }

        }
    .
     
  11. And I removed the Cancel for tests
     
  12. on which version is your server running?
     
  13. Ok now it works. I dont know what I did wrong before... Thanks to Vibemaster for your Help and Tipps!

    Closed
     
  14. Hey there, I was wondering if you ever managed to finish this plugin, its something that i've been looking for to include in my server.
     
  15. Yeah I have a working plugin version now :D
     
  16. Do you know where I'd be able to find this?
     
  17. idk if you understand java but here is the code:
    Code (Java):
    @EventHandler
        public void onVillagerInteract(final PlayerInteractAtEntityEvent e) {
            if (!(e.getRightClicked() instanceof Villager)) return;

            Villager villager = (Villager) e.getRightClicked();

            /* This uses google guava (which spigot includes) */
            /* google guava, just takes the elements from the list and puts it into a new list (that we can modify) */
            final List<MerchantRecipe> recipes = Lists.newArrayList(villager.getRecipes());

            /* Convert the list to an iterator so we can safely remove values from it while looping through the iterator */

            Iterator<MerchantRecipe> recipeIterator;
            for (recipeIterator = recipes.iterator(); recipeIterator.hasNext(); ) {
                MerchantRecipe recipe = recipeIterator.next();

                if (recipe.getResult().getType().equals(Material.ENCHANTED_BOOK)) {

                    EnchantmentStorageMeta meta = (EnchantmentStorageMeta) recipe.getResult().getItemMeta();

                    for(String ench : plugin.getConfig().getStringList("EnchantmentsToRemove")){
                        if (meta.hasStoredEnchant(Objects.requireNonNull(Enchantment.getByName(ench)))) {
                            recipeIterator.remove();
                        }
                    }
                }
            }

            villager.setRecipes(recipes);

            if (villager.getRecipes().size() == 0) {
                e.setCancelled(true);
            }
        }