Solved Assigning permissions

Discussion in 'Plugin Development' started by Romain TV, Feb 25, 2023.

Thread Status:
Not open for further replies.
  1. Offline

    Romain TV

    Hello, I recently started learning how to make plugins last week, and I spent around 50 hours in overall on it. That's a lot of informations but you gotta start somewhere. All of that to say, I've obviously encountered many issues, but eventually, I fixed all of them but one. I'm making a plugin that creates roles for SMPs, 6 roles having different permissions. Like the Miner, who can mine gold, redstone, diamond, emerald, deepslate variants and ancient debris. The Listeners, are made, I even filled the config file with the perms, but I have no idea how to assign these perms. I seriously spent 13 hours these last 2 days reading a lot of threads, watching tutorials, but nothing really comes up with what I need,

    Here's the example of the miner perm in the listener :
    Code:
    public class EventsRoles implements Listener {
    
        @EventHandler
        public void blockBreak(BlockBreakEvent event) {
            Player player = event.getPlayer();
            Material material = event.getBlock().getType();
            if (!player.hasPermission("minerpermission")) {
                if (material.equals(Material.GOLD_ORE) || material.equals(Material.DIAMOND_ORE) || material.equals(Material.EMERALD_ORE) || material.equals(Material.REDSTONE_ORE) || material.equals(Material.DEEPSLATE_GOLD_ORE) || material.equals(Material.DEEPSLATE_DIAMOND_ORE) || material.equals(Material.DEEPSLATE_EMERALD_ORE) || material.equals(Material.DEEPSLATE_REDSTONE_ORE) || material.equals(Material.ANCIENT_DEBRIS)) {
                    event.setCancelled(true);
                    player.sendMessage("§cYou are not a Miner, you do not have the experiece to break §e" + material + " §c!");
    
                }
            }
        }
    I wrote a small code that checks perms in the config file and gives me all of them, and it works, but if I try to assign it to a player via an EventHandler like this one :

    Code:
    @EventHandler
        public void onClick(InventoryClickEvent event) {
    
            Inventory inv = event.getInventory();
            Player player = (Player) event.getWhoClicked();
            ItemStack current = event.getCurrentItem();
    
            if (current == null) return;
            if (event.getView().getTitle().equalsIgnoreCase("§8Roles")) {
    
                event.setCancelled(true);
    
                if (current.getType() == Material.BLUE_STAINED_GLASS_PANE || current.getType() == Material.LIME_STAINED_GLASS_PANE || current.getType() == Material.BROWN_STAINED_GLASS_PANE || current.getType() == Material.RED_STAINED_GLASS_PANE || current.getType() == Material.BLACK_STAINED_GLASS_PANE || current.getType() == Material.YELLOW_STAINED_GLASS_PANE) {
                    player.sendMessage("§cYou can't do that !");
                }
                if (current.getType() == Material.IRON_PICKAXE) {
                    player.sendMessage("§aYou chose to become a §9Miner §a!");
                    TestingThis.addToRoles(TeamType.MINER, player);
                    player.setPlayerListName("§a[§9§lMINER§r§a]§r " + player.getName());
                    Bukkit.broadcastMessage("§a[§9" + player.getName() + "§a] has chosen the §9Miner §4Role §a!");
                    player.closeInventory();
    And add a line that basically states "give player minerpermission" but there's always an issue.
    Whether it is because I need JavaPlugins extension, so I made it in the Main Class, but I can't make them static, cuz it uses "(Plugin) this" so I implemented Listener to the class, but then, if I move the EventHandler to it, it cease to work. I know there has to be a simple way to make such a dumb permission, to make my plugin independent from LuckPerms or another. However, I'm unable to find it. So hard in fact, I made a bukkit account to ask you pros (I wasn't planning on using my code for anything else than playing it with my friends, but having it being independent would be a huge POG, and I would FINALLY know how to do it).

    If you have the awnser, I would love to know it.

    P.S. : Yes, I read
    https://bukkit.fandom.com/wiki/Developing_a_permissions_plugin
    and
    https://bukkit.fandom.com/wiki/Understanding_Permissions
     
  2. Offline

    Strahan

    I'm confused; are you trying to check permissions to perform an action, or are you trying to make a permissions plugin? I assume the former even though your PS links are for the latter. No wonder you are confused if that is what you were reading.

    Code:
    if (!player.hasPermission("minerpermission")) {
    if (material.equals(Material.GOLD_ORE) || material.equals(Material.DIAMOND_ORE) || material.equals(Material.EMERALD_ORE) || material.equals(Material.REDSTONE_ORE) || material.equals(Material.DEEPSLATE_GOLD_ORE) || material.equals(Material.DEEPSLATE_DIAMOND_ORE) || material.equals(Material.DEEPSLATE_EMERALD_ORE) || material.equals(Material.DEEPSLATE_REDSTONE_ORE) || material.equals(Material.ANCIENT_DEBRIS)) {
    event.setCancelled(true);
    player.sendMessage("§cYou are not a Miner, you do not have the experiece to break §e" + material + " §c!");
    }
    }
    That's the right idea, but I'd suggest that you not use single nodes like that. Do pluginname.permissionnode instead. Next question, why are you worrying about the material if they have no permission to mine? If they aren't allowed to mine, the material is irrelevant. Next thing, you should read up on collections. That if statement is ugly AF. You could make a collection of Material and then reference that instead.

    I don't even see a perm check in the second block of code. In that code block, is the huge IF that checks glass simply to stop people from clicking non-relevant items? Usually glass is a filler, so I assume that's what you are doing. If so, that isn't necessary. Just do not perform an action if it isn't a valid item clicked.

    Lastly, you should not be embedding §. The ChatColor enum exists for that, use that instead. Another thing to put on your list of things to read up on would be how to use config files. If you intend to eventually share this plugin, you should not be hardcoding the messages. Use the config to load them so people can localize your plugin if so desired. Plus it makes it a lot easier to maintain, even if you don't distribute it. If you decide you want to change wording, you can just make a quick and easy change in a text file rather than having to fire up the IDE, recompile and replace a plugin.

    I try to never hardcode stuff if I can avoid it.

    PS: do you have a permissions manager installed? If not, get one. I'd suggest LuckPerms, it's very good.
     
  3. Offline

    Romain TV

    First, thank you for replying. I want to quickly mention, I want my plugin to be independent, I know it's annoying, but I want to learn how to do things like that on my own.
    Second, yes I know, my code looks messy af, but I wanted to make it fully functionnal first, and then makes it a little better, (althought I was really putting all materials in if statements seriously, and thank you for the technics). Anyways, as I said at the end of my first message, I want to avoid using other plugins, again I want to learn how to do perms, as I assume it's possible.

    So, now that we're done, let's talk about the second if statement, it's just an EventHandler for a GUI where people can choose their role.
    Code:
    if (current.getType() == Material.IRON_PICKAXE) {
                    player.sendMessage("§aYou chose to become a §9Miner §a!");
                    TestingThis.addToRoles(TeamType.MINER, player);
                    player.setPlayerListName("§a[§9§lMINER§r§a]§r " + player.getName());
                    Bukkit.broadcastMessage("§a[§9" + player.getName() + "§a] has chosen the §9Miner §4Role §a!");
                    player.closeInventory();
                }
    Yes, it is messy, I know you want to cry, I'll make changes once it works like changing §9 for ChatColor.BLUE. Anyway this item, the Iron pick (which will be customisable via the config), is the one that'll give you the perms to be a miner. As said before in the first inserted code of my first message, The miner will be the only one able to break Gold, Redstone, Diamond, Emerald ores and theirs Deepslate variants. My plugin is just adding roles to SMPs with different perms. With all you've seen, I just want, when they select the Iron Pick, to give them what I call "minerpermission" which will allow them to break said ores. Is it even possible ?
     
  4. Offline

    Strahan

    Gotcha. Makes sense, but that kind of thing is best left for after you are more fluent with the language. Security stuff like permissions managers are not projects new developers should be tackling. Having a permissions manager installed is not a disadvantage in any way, you aren't really gaining any advantage by avoiding using one.

    Yup. I don't mean you need to make it perfect immediately, hehe, just wanted to explain ways it could be optimized so you know.

    Well, for the pick to "give the perms" would mean you are tying permission to perform an action to an item. That's not actually a "permission" in the sense of the normal server nomenclature. To achieve it, I'd personally do by keeping a collection of the special items in the config. The special items would have a String PDC tag with the key name of the item. During the block damage event, I'd check if the item being used is a special item. If it is, I'd pull a list of valid materials from the config using the PDC key and if it dosen't match, cancel the event.

    Now if you just mean clicking the pick makes them a miner and it has nothing to do with the item, then I'd have it add the player to a collection of miners when the pick is clicked. Then in the block damage event, check if the player is in the appropriate collection. If not, cancel.
     
  5. Offline

    Romain TV

    You're a genius man, that's exactly it. The iron pick, along with the fillers (stained glasses) are just a part of the selection role GUI. "Collection" the term you used might be what I'm looking for. I'm calling them "perms" but that's not perms, it's just whether they have the right to break said ores or not (Using Miner as example). If you have any info or links to good tutorials, articles explaining it in details, I would appreciate it. I'll start learning about it and see if it's what I'm looking for. Thank you, and I'll keep you up about it.
     
  6. Offline

    Strahan

    Not sure about tutorials. How are you tracking miners? Or have you not gotten that far yet? If I were doing a class/job type of plugin, I'd probably approach it by making a collection of jobs and players with jobs. Like
    Code:
    Map<String, Job> jobs = new HashMap<>();
    Map<UUID, List<Job>> playerJobs = new HashMap<>();
    Then I'd create a custom object for job data:
    Code:
    public class Job {
      private String name = "";
      private List<Job> incompatibleJobs = new ArrayList<>();
    
      @Override
      public boolean equals(Object obj) {
        return obj instanceof Job ? ((Job)obj).name.trim().equalsIgnoreCase(name.trim()) : false;
      }
    
      public Job(String name) {
        this.name = name;
      }
    
      public boolean isIncompatible(Job job) {
        return incompatibleJobs.contains(job);
      }
    }
    
    public class Miner extends Job {
      private List<Material> breakList = new ArrayList<>();
      private int digSpeedBonus = 0;
      private int lootBonus = 0;
      // etc etc whatever
    
      public boolean canBreak(Material m) {
        return breakList.contains(m);
      }
    }
    So then I'd make my job selection inventory, and populate it using something like this:
    Code:
    public ItemStack getInventoryItem(String material, String title, List<String> lore, String job) {
      Material m = Material.matchMaterial(material);
      if (m == null) m = Material.BARRIER;
      ItemStack i = new ItemStack(m);
      ItemMeta im = i.getItemMeta();
      im.setDisplayName(ChatColor.translateAlternateColorCodes('&', title));
      im.setLore(lore);
      im.getPersistentDataContainer().set(jobKey, PersistentDataType.STRING, job);
      i.setItemMeta(im);
      return i;
    }
    Then in the inventory click,
    Code:
    click event {
      // check if it's the right inv
      // do safety checks (null items, action, hand, etc)
    
      Job j = UtilityClass.getJob(itemStackClicked);
      if (j == null) { throw error and return }
    
      if (UtilityClass.addJob(e.getWhoClicked(), j)) {
        send "you are now this job" message
        return;
      }
    
      send "you already are a (job)!"
    }
    Utility class:
    Code:
    public Job getJob(ItemStack i) {
      if (i == null) return null;
      if (!i.hasItemMeta()) return null
      if (!i.getItemMeta().getPersistentDataContainer().has(jobKey, PersistentDataType.STRING)) return null;
    
      return getJob(i.getItemMeta().getPersistentDataContainer().get(jobKey, PersistentDataType.STRING));
    }
    
    public Job getJob(String job) {
      return jobs.getOrDefault(job, null);
    }
    
    public Job getJob(Player p, String job) {
      Job j = getJob(job);
      return j == null ? null : (playerJobs.getOrDefault(p.getUniqueId(), new ArrayList<>()).contains(j) ? j : null);
    }
    
    public boolean addJob(Player p, Job j) {
      UUID u = p.getUniqueId();
      if (!playerJobs.containsKey(u)) {
        playerJobs.put(u, new ArrayList<>(Arrays.asList(j)));
        return true;
      }
    
      if (playerJobs.get(u).contains(j)) return false;
    
      playerJobs.get(u).add(j);
      return true;
    }
    Then in a block damage event
    Code:
    BlockDamageEvent {
      Job j = UtilityClass.getJob(e.getPlayer(), "miner");
      if (!(j instanceof Miner)) { throw error about not being a miner, cancel event, and return; }
      if (((Miner)j).canBreak(e.getBlock().getType()) return
    
      throw error about material, cancel event
    }
    Now this is all simplified and off the top of my head (I didn't type it first in my IDE to ensure I'm remembering methods correctly) but it's just to give you an idea of how I personally would do it.
     
    Last edited: Feb 28, 2023
    mehboss likes this.
  7. Offline

    Romain TV

    Ok, bro, I love you. That is EXACTLY what I needed to understand. I already setted up something like that in my code, but I just understood everything I could do thanks to your explanations. I made the code work, I'm gonna make something similar for the entire plugin and then, I'll make it fully customizable and improve it's structure ;). Thank you so much again, it just opened a door to plugin development. Yes I'm exited to see if I can make it fully functionnal. I didn't know I could create "permissions" using arraylist and whether players were in there or not.
    Indeed, it is how you do it, I was just too dumb to see it.

    Althought, it doesn't mean I'm done lol. I still have a question.

    How do I keep them in the arrayList ? I mean, I've seen plenty of people saying on disconnection event, they should be removed from any list, so that there's no duplicates. But, they get added by clicking on the Iron Pick to Miner Role or whatever, not when they join. Like it's not a thing they could abuse to crash the plugin. So my question is :

    Can I keep them in the array list or do I need to like save their UUID somehow and remove them from the array list, and add them back once the rejoin via PlayerJoinEvent ?
    (Any tutorial or code provided if I need to do all of that would be as much appreciated as with the custom collection tutorial :D).
     
    Last edited: Feb 28, 2023
  8. Offline

    mehboss

    Hello! I would personally recommend saving UUID info into a yml file to store back into an HashMap whenever the plugin reloads. You should be able to avoid removing them and adding them back in every join/quit since their job title will not change simply by logging out.
     
    Last edited: Mar 1, 2023
    Strahan likes this.
  9. Offline

    Strahan

    As mehboss said, use UUID. Names can change, UUIDs are fixed. The people saying about disconnect, they're spot on. Any time you have a collection of player data, you need to clean it else it will grow and grow. So like in my example, I have a Map<UUID, List<Job>> so I'd do this:
    Code:
    @EventHandler
    public void buhbye(PlayerQuitEvent e) {
      if (!playerJobs.containsKey(e.getPlayer().getUniqueId()) return;
    
      playerJobs.remove(e.getPlayer().getUniqueId());
    }
    As far as the arraylist vs map, you could do just an ArrayList if you make the data it is storing taggable by the user, but IMO it adds unnecessary complication. You wouldn't want to make Job have player info; the Job object should be only job related info. Player extends PersistentDataHolder so you could possibly encode their jobs there, or make a custom object to hold the relation.

    But all that can be avoided by simply using a Map instead of a List and keying on the player's UUID. That makes it very simple to match up.
     
    mehboss likes this.
  10. Offline

    mehboss

    Whoops. I didn't mean to say ArrayList. I meant to say HashMap :D
     
  11. Offline

    Romain TV

    Sorry, I had to take finals a little seriously... Anyway, thank you a lot, it helped and taught me plenty.
     
Thread Status:
Not open for further replies.

Share This Page