Solved Storing data (HashMaps)

Discussion in 'Plugin Development' started by Xp10d3, Feb 27, 2020.

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

    Xp10d3

    Hello. I am confused about HashMaps and how to use them. I am trying to store data in my player listeners class (specifically the X, Y, and Z values) and then access them in my commands class to claim a plot.

    PlayerListeners.java:
    Code:
    package add.plots.xp10d3;
    import java.util.ArrayList;
    import org.bukkit.Material;
    import org.bukkit.event.EventHandler;
    import org.bukkit.event.Listener;
    import org.bukkit.event.block.Action;
    import org.bukkit.event.player.PlayerInteractEvent;
    import org.bukkit.inventory.ItemStack;
    import org.bukkit.inventory.meta.ItemMeta;
    public class PlayerListeners implements Listener {
       
        @EventHandler
        public void onBlockClick(PlayerInteractEvent event) {
            ItemStack wand = new ItemStack(Material.BLAZE_ROD);
            ItemMeta wandMeta = wand.getItemMeta();
            wandMeta.setUnbreakable(true);
            wandMeta.setDisplayName("PLOT WAND");
            ArrayList<String> wandLore = new ArrayList<>();
            wandLore.add("Lore");
            wandMeta.setLore(wandLore);
            wand.setItemMeta(wandMeta);
            if (event.getAction() == Action.LEFT_CLICK_BLOCK) {
                int x = event.getClickedBlock().getX();
                int y = event.getClickedBlock().getY();
                int z = event.getClickedBlock().getZ();
            }
            if (event.getAction() == Action.RIGHT_CLICK_BLOCK) {
                int x2 = event.getClickedBlock().getX();
                int y2 = event.getClickedBlock().getY();
                int z2 = event.getClickedBlock().getZ();
            }
        }
    }
    
    
    What I have of my Commands.java:
    Code:
    package add.plots.xp10d3;
    import org.bukkit.command.Command;
    import org.bukkit.command.CommandExecutor;
    import org.bukkit.command.CommandSender;
    import org.bukkit.entity.Player;
    import net.md_5.bungee.api.ChatColor;
    public class Commands implements CommandExecutor {
       
        @Override
        public boolean onCommand(CommandSender sender, Command cmd, String lable, String[] args) {
            Player player = (Player) sender;
            PlayerListeners playerListeners = new PlayerListeners();
            if (!(sender instanceof Player)) {
                sender.sendMessage(ChatColor.RED + "You must be a player to execute this command!");
            }
            if (cmd.getName().equalsIgnoreCase("claim")) {
               
            }
            return false;
        }
    }
    
     
  2. Offline

    MCMastery

    Use Map<UUID, Vector>
    then to get the vector from a player use map.get(player.getUniqueId())
    to set the vector use map.put(player.getUniqueId(), vector)
     
    Xp10d3 likes this.
  3. Offline

    bowlerguy66

    You're going to have to set up a global variable like this:
    Code:
    HashMap<UUID, Vector> locations = new HashMap<UUID, Vector>();
    And then you'll use locations.put() to store your variables. To get your data you can use locations.get(uuid) to return a Vector value. Make sure you pass the map through your constructors properly so you don't get any NPEs. Also, it's important to note that HashMaps will be reset after restarts/reloads so you may have to use FileConfiguration to save your data permanently if you need it. Good luck!
     
    Xp10d3 likes this.
  4. Offline

    Strahan

    As mentioned, you need the variable scope to be something that will persist.

    You should declare that as Map<UUID, Vector> locations instead. It's part of the SOLID design principles; the L - Liskov substitution principle that states that objects of a superclass shall be replaceable with objects of its subclasses without breaking the application. So use Map as its the "root" and like for an ArrayList, on the left you'd use List, etc.

    OP, some comments on your code:
    Code:
    ArrayList<String> wandLore = new ArrayList<>();
    wandLore.add("Lore");
    wandMeta.setLore(wandLore);
    You can simplify that to one line as wandMeta.setLore(new ArrayList<String>(Arrays.asList("Lore")));

    Code:
    if (event.getAction() == Action.LEFT_CLICK_BLOCK) {
      int x = event.getClickedBlock().getX();
      int y = event.getClickedBlock().getY();
      int z = event.getClickedBlock().getZ();
    }
    if (event.getAction() == Action.RIGHT_CLICK_BLOCK) {
      int x2 = event.getClickedBlock().getX();
      int y2 = event.getClickedBlock().getY();
      int z2 = event.getClickedBlock().getZ();
    }
    Any time you find yourself duplicating code, it's a sign something is probably not laid out right. Your code doesn't show what you do with the variables, but this can likely be optimized.

    Code:
    Player player = (Player) sender;
    PlayerListeners playerListeners = new PlayerListeners();
    if (!(sender instanceof Player)) {
    Checking the sender is a Player after casting it thus isn't very logical. That should be done first. Also I assume by the name PlayerListeners is an event listener, right? If so, why are you creating it every time a command is ran? That doesn't seem like a logical design.
     
    Xp10d3 likes this.
  5. Offline

    Xp10d3

    But if I am trying to get the block's location that the player left/right clicks, I don't want to use the UUID/Vector, correct? What would I use instead? Thanks for the help :)

    Whoops; thanks for the catch :) I forgot I set the Player variable. And thanks for the suggestions :p
     
  6. Offline

    bowlerguy66

    Using the uuid of the player will allow you to retrieve your data based on the player, and the vector allows you to get the X, Y, and Z locations of the block. You could also use the Location value of the block if you wanted.
     
  7. Offline

    Xp10d3

    Bit confused on how the Vector works. I read a bit of the Bukkit docs and all I got was BlockVector and Location, but the Location is only toLocation() and that’s not what I want. Same with BlockVector since that’ll just be the player touching a block I believe. Or is that wrong...?


    Sent from my iPhone using Tapatalk
     
  8. Offline

    Xp10d3

    Bump
     
  9. Offline

    Machine Maker

    Basically, if you want to be able to access the last clicked location by a player in your commands class, you have to store that information in such a way that it is accessible to both.

    What I would do, would be to create a HashMap<UUID, Location> that stores the player-who-clicked's UUID (as a way of identifying them), as well as the Location object of the location where they clicked.

    This Map has to be defined in a place that you can get access to it from either the Event Listener, or the command "listener".

    I would define it in the MAIN class for your plugin, create a getter (to get the Map) and an adder (to Add something to the Map) in the main class, and pass your main class (using "this") into constructors for both the Listener class, and the Command class.
     
  10. Offline

    Xp10d3

    Sorry for the late reply. I'm confused on getting the value. I have this so far:
    Code:
    
    package add.plots.xp10d3;
    import java.util.ArrayList;
    import org.bukkit.Material;
    import org.bukkit.entity.Player;
    import org.bukkit.event.EventHandler;
    import org.bukkit.event.Listener;
    import org.bukkit.event.block.Action;
    import org.bukkit.event.player.PlayerInteractEvent;
    import org.bukkit.inventory.ItemStack;
    import org.bukkit.inventory.meta.ItemMeta;
    public class PlayerListeners implements Listener {
       
        Core core = new Core();
       
        @EventHandler
        public void onBlockClick(PlayerInteractEvent event) {
            Player player = event.getPlayer();
            ItemStack wand = new ItemStack(Material.BLAZE_ROD);
            ItemMeta wandMeta = wand.getItemMeta();
            wandMeta.setUnbreakable(true);
            wandMeta.setDisplayName("PLOT WAND");
            ArrayList<String> wandLore = new ArrayList<>();
            wandLore.add("Lore");
            wandMeta.setLore(wandLore);
            wand.setItemMeta(wandMeta);
            if (event.getAction() == Action.LEFT_CLICK_BLOCK) {
                core.locations.put(player.getUniqueId(), event.getClickedBlock().getX());
                core.locations.put(player.getUniqueId(), event.getClickedBlock().getY());
                core.locations.put(player.getUniqueId(), event.getClickedBlock().getZ());
            }
            if (event.getAction() == Action.RIGHT_CLICK_BLOCK) {
                core.locationsTwo.put(player.getUniqueId(), event.getClickedBlock().getX());
                core.locationsTwo.put(player.getUniqueId(), event.getClickedBlock().getY());
                core.locationsTwo.put(player.getUniqueId(), event.getClickedBlock().getZ());
            }
        }
    }
    
    
    When I do core.locations.get() I'm confused on what to get. It only lets me get one value but I'm confused on how to get the clicked block locations.
     
  11. Offline

    Machine Maker

    So, you have a couple problems.

    1. Depending on what exactly your Core class is, you can't create it where you did (above the event handler for PlayerInteractEvent). If Core is your main plugin class, you just want to pass the instance of your main class (using "this") into the constructor for PlayersListeners. Then in the constructor for PlayersListeners you can set a local variable there.

    2. You are misunderstanding how Maps work. They have unique keys (that means there cannot have duplicate keys in the map), so instead of having a Map between the player UUID and a double, just have a map between the Player UUID and the Location object itself. Because yes, you are totally right, you can only get 1 value when you do locations.get(UUID). So you want that one value to be the Location object.
     
  12. Offline

    Wick

    You have a map with a key value of UUID and an object value of location.

    To place a clicked location:

    Code:
    locations.put(player.getUniqueId, event.getClickedBlock.getLocation)
    To retrieve a player's clicked location you use a key (which is UUID) to retrieve an object (the location paired with a player)

    Code:
    locations.get(player.getUniqueId)
    If you want all the clicked locations for every player then retrieve the map

    Code:
    Map<UUID, Location> clickedLocations = core.locations
    Also, rename your methods "locations" and "locationsTwo" to something more appropriate, as these methods are getters, they should have 'get' at the front, makes code more readable

    Code:
    locations --> getLocationOne
    locationsTwo --> getLocationsTwo
    EDIT: nvm, assumed they were methods accessing private maps
     
    Last edited: Mar 3, 2020
  13. Offline

    Machine Maker

    Well actually, those aren't methods. I'm assuming they are just public fields in the Core class.
     
  14. Offline

    Xp10d3

    Alright. I kind of just used another plugin. Thanks anyways 0-0
     
Thread Status:
Not open for further replies.

Share This Page