Solved Little bast... erm Hashmap issue

Discussion in 'Plugin Development' started by Luke_Lax, Dec 11, 2013.

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

    Luke_Lax

    Hey all, I'm having one of those problem where nothing works! :'(

    Here's situation and needs: I have a BukkitRunnable which works fine and all, it's executed when a block is placed, great! However, when a person breaks that block I want that (*and only that*) repeating task to end... This is where my issue is.

    I thought the best way to end the specific repeating task would be to: Add the taskID to a hashmap, with the key being the block location, can't go wrong right?
    Well... The location was the same every time and I made sure of that with a debug message. To aid that debug message, I made sure that the task was actually in the hashmap, so I made the debug print the whole hashmap list and it was there. I don't get why my hashmap.get(Location) won't get the value?

    Here's my initialization of my hashmap:
    Code:
    HashMap<Location, Integer> TDs = new HashMap<Location, Integer>();
    
    Here's where I put the location and ID into the hashmap within a blockplaceevent(fine):
    Code:
    Loc = event.getBlock().getLocation();
    BukkitScheduler scheduler = Bukkit.getServer().getScheduler();
    taskID = scheduler.scheduleSyncRepeatingTask(this.main, new BukkitRunnable() {
       @Override
             public void run() {
                //RUN
             }
         }, 20, 5);
    TDs.put(Loc, taskID); 
    
    and finally, here's me trying to remove it, upon a blockbreakevent:
    Code:
    Location Loc2 = event.getBlock().getLocation();
    if(TDs.containsKey(Loc2)){
               Bukkit.getScheduler().cancelTask(TDs.get(Loc2));
      } else {
      Bukkit.broadcastMessage("Not found, here's the hashmap:  "+TDs.values().toString()); //Debug message, shows the taskID :3
      }
    }
    }
    

    Please help! All help is appreciated, if you know of an easier way to cancel individual repeating bukkitRunnables then please, save my hair falling out -- :p
    Thanks in advance!
     
  2. Offline

    scenecraft

    Can you Please have Error Log
     
  3. Offline

    Luke_Lax

    Well there is no error as my else statement prevents it :confused: The issue is that it's not getting the value from the hashmap, that is my error! :p
     
  4. Offline

    NathanWolf

    Weird, that seems like it should work! The only thing I can think of is maybe that Location includes yaw and pitch as well.. I'd expect those to both be 0 in the case of getBlock().getLocation(), but just in case maybe check that?
     
  5. Offline

    Luke_Lax


    Hmm you're right, it does include yaw, pitch, world and craftbukkit information - I'll create a variable using just getX, getY and getZ and report how that goes :)

    NathanWolf
    Ok I tried using: double ff2 = (Loc.getX() + Loc.getY() + Loc.getZ());
    I get that that's adding the 3 values but that should still be unique to the block, however I found (using debug statements) that if I place it I'll get a number (for example) 605.0 then when I break it, it says it's block 602.0, why has it taken 3 off of the number? :confused:
     
  6. Offline

    The_Doctor_123

    You see, this is a little bit of an OOP thing I'll need to explain to you. You're trying to see if a HashMap contains a key with a Location of that Block. Notice how I said "a." There can be multiple different objects with exact same values contained inside of them. However, they're still different objects, and nothing can change that. When you call getLocation() in Block, it returns a COPY of the Location. Well if that's the case, then you're never going to get the same Location that's already in the HashMap's keyset since getLocation() always returns a copy!

    So you're going to need to rethink your code a little bit. Hope that helped. :)
     
  7. Offline

    JRL1004

    Luke_Lax The would require the block to be off b7 three. Are you certain you are breaking the same block and storing the locations uniformly (E.g. as a string of "X/Y/Z")?

    EDIT: Maybe "World/X/Y/Z" so that you don't derp over dimensions?
     
  8. Offline

    Luke_Lax

    The_Doctor_123 Hmm I get what you're saying but I don't see a work around, perhaps a hint?

    JRL1004 ha, only one world, I'm in a glass sphere breaking the only Ender portal frame around :p Oh and yeah, the getX, getY, getZ are the same in both the place and break (just a bit of copy and paste magic)
     
  9. Offline

    NathanWolf


    Well, there's a first time for everything, I but I think I have to disagree with you here!

    Location overrides equals() and hashCode(), I believe for this specific purpose (hashing Locations and having it work like you'd expect). From what I understand, HashMap will internally use those functions- and Bukkit has made it so Locations are equivalent if x,y,z,world,yaw,pitch are all equivalent.

    Which is why I'm confused... Though I have to admit I've never actually tried to put them in a hash set/map before.

    That's a bit flawed - (1,0,0) and (0,0,1) and (0,1,0) will all map to the same place (for instance- there are more..)

    Generally when you do this kind of thing you do it with ints, not floats, and use bit arithmetic and not decimal- e.g. long hash = y | (x << 8) | (z << 40);

    If my math is right, that would pack 1-byte of y-value and two 32-bit x,z values ... but the result is then a 65-bit integer, which doesn't work out well.

    To do this properly you'd have to do something along the lines of what Bukkit did in Location- override hashCode and equals to check equivalency. Since MC's coordinates get quite large, you're not going to be able to fit all three in one (integer) datatype... and don't forget about the world name! :p

    But.. once you've gone through all that, seems like you're back where you started, almost. I'm curious why it isn't working the way you're trying it, I feel like we're missing something.
     
  10. Offline

    JRL1004

    Luke_Lax Instead of using getX(), getY(), and getZ(), would using getBlockX(), getBlockY(), and getBlockZ() work? I know there is a difference but I can't remember what it is at this moment.
     
    NathanWolf likes this.
  11. Offline

    NathanWolf


    You should definitely be using the getBlock() methods - they just return the nearest integer coordinate, but for blocks that's all that matters.
     
  12. Offline

    The_Doctor_123

  13. Offline

    NathanWolf

    I was thinking the same thing!

    Seems to work... I'd double-check yaw and pitch just in case they are set to something funky in those events, for some reason... otherwise I'm at a loss.

    Code:java
    1.  
    2. Block block = player.getLocation().getBlock();
    3. Location equivalent = new Location(block.getWorld(), block.getX(), block.getY(), block.getZ());
    4. HashMap<Location, Integer> testing = new HashMap<Location, Integer>();
    5. testing.put(block.getLocation(), 1234);
    6. if (testing.containsKey(equivalent)) {
    7. player.sendMessage("it worked: " + testing.get(equivalent));
    8. }
    9.  
     
  14. Offline

    Luke_Lax

    NathanWolf The_Doctor_123
    I changed to .getBlockXYZ(), however I had the same issue (the BreakMethod, although it uses the same .getBlockXYZ) it returned a value 2 less than the original, I know this isn't good practise and I wouldn't keep it, but for testing I simply add + 2 to the breakmethods getblock.. so they both returned the same value, however it still could not get the Value? This is really odd

    Oh weird, just looking more closely at the debug messages: http://puu.sh/5JMy4.png
    The X is off by 1.5 in the onBreak and the Y is off by 2. Why's that? (The latter in the picture is the break method's debug and the first is the Place methods debug)

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 5, 2016
  15. Offline

    Sagacious_Zed Bukkit Docs

    Luke_Lax

    Since you have defined a BukkitRunnable you should execute the runTaskTimer method on the BukkitRunnable instead of feeding the BukkitRunnable to the scheduler yourself.

    BukkitRunnable has a cancel method defined, but it is only valid to call if you execute the run method on the BukkitRunnable.
     
  16. Offline

    The_Doctor_123

    NathanWolf
    You know, after I made my post, I was scratching me head and going "How isn't there other noobs making these kinds of mistakes?" In this case, I was too smart. D:

    Luke_Lax
    That doesn't make much sense.. Current code?
     
    NathanWolf likes this.
  17. Offline

    Luke_Lax

    Sagacious_Zed
    I'll be blunt and say: I have no clue what this means :3
     
  18. Offline

    NathanWolf

    Hm- wait, so just to be clear- are you modifying Loc anywhere after adding it to the map? (You shouldn't be)
     
  19. Offline

    Luke_Lax

    Nope, not at all! I'm using the exact same method in the same order to get the location; heck, I even copied your location because you included the world (Which I forgot about, thanks :p not that, that fixed my issue but small details count too! :D)
     
  20. Offline

    Sagacious_Zed Bukkit Docs

    Wingzzz likes this.
  21. Offline

    The_Doctor_123

    Luke_Lax
    Which Location is incorrect? The one in the keyset or the one you get from the Block?
     
  22. Offline

    Luke_Lax

    How would I call that? Because my run requires the location of the block and I can't pass any arguments in the run(<here>) because I'll get an error to add unimplemented methods? Unless I've misunderstood as your example it's very friendly looking :p!

    The_Doctor_123
    When the method to get the coords is executed in the Blockbreakevent, it's wrong, yet the method is the exact same in both the place and break:
    Code:
    Location equivalent = new Location(Loc.getWorld(), Loc.getX(), Loc.getY(), Loc.getZ());
    Edit: Yikes, how many screw ups can I make in one post? #4 edits
     
  23. Offline

    Sagacious_Zed Bukkit Docs

    Luke_Lax Your current BukkitRunnable is anonymous. If you had created a BukkitRunnable that is a top level class, the methods you would be calling in your anonymous BukkitRunnable would be out of scope. This is the case where you need to adapt the example to work with your existing code.
     
  24. Offline

    Luke_Lax

    Sorry, don't take me the wrong way, I'm very grateful that you're trying to help me, but I honestly don't get that. Are you telling me that if I put the runnable in another class on it's own, extending BukkitRunnable that I could do a block presence check then use this.close based on the result? If so, could you explain how I could give the location argument, it has to have that as the runnable performs a series of operations that affect that location, it's not just something I can call randomly like "Bukkit.broadcastMessage("Hey Bukkit dev")" :p
     
  25. Offline

    Sagacious_Zed Bukkit Docs

    Luke_Lax
    No, If you move the BukkitRunnable into its own top level class, I am saying the method is no longer in scope, therefor it is undefined for what the keyword 'this' is referencing. Since the keyword 'this' is special, and always refers to the current instance of the class that the block of code is located in.

    On a completely different note, you can give the location argument, just as the counter argument is given in the example. EDIT: However if you choose to keep the definition of your BukkitRunnable, you cannot define a custom constructor. Which means you have to have a reference to your Location in the surrounding block.

    Also, all the methods in Bukkit are globally accessible, thus you did not need an instance of the Bukkit class, you could call them directly on Bukkit.
     
  26. Offline

    Luke_Lax


    I'm sorry I'm just going to have to accept that I can't do this. You're speaking another language and I've no clue where to put what now so yeah, but thanks anyway, everyone :(

    The_Doctor_123 NathanWolf Sagacious_Zed
    Ok, last night this just stressed me too much. However I really need this working - I did some more research and found this example:
    Code:
    class MyRunnable implements Runnable
    private final Plugin plugin;
    private int id;
     
    public MyRunnable(Plugin plugin) { this.plugin = plugin; this.id = -1;}
     
    public void start() {
    id = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this, 20, 20);
    }
     
    public void stop() {
    plugin.getServer().getScheduler().cancelTask(id);
    id = -1;
    }
     
    @Override
    public void run() {
    // your stuff
    }
    
    However, I don't understand how, using this example, I'd be able to pass the Location which is required. How would I pass the Location? If I change it to "public void run(final Location loc)" it gives me an error to "Add unimplemented methods" which just generates the run block again :(
    Perhaps someone could decipher Sagacious_Zed's talk? I don't understand his example what so ever nor most of what he said but I bet if I did, it'd be able to do it :/

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 5, 2016
  27. Offline

    Sagacious_Zed Bukkit Docs

    @Luke_Laxn that example looks like it is from a time before BukkitRunnable existed. BukkitRunnable has methods to start and stop itself.
     
  28. Offline

    Luke_Lax

    Sagacious_Zed Yes I believe it was before then :) I know with a bukkitrunnable you can use this.cancel, but if I do a check to make sure the block is still there I get an error saying that the task has not been initialised?
     
  29. Offline

    Sagacious_Zed Bukkit Docs

    As I have said before, don't feed the BukkitRunnable to the scheduler to have it be executed. Invoke the proper run method on the BukkitRunnable instead.

    For an example see http://wiki.bukkit.org/Scheduler_Programming#Self-Canceling_Example_2
     
  30. Offline

    Luke_Lax

    @The_Doctor_123 @NathanWolf @Sagacious_Zed
    :/ I'm just not understanding, what have I done wrong without all the gobbly goop language? :p And please don't refer to your example as they're just confusing, so how would I, in my situation call this run method when a block is placed and cancel it when that block is broken? I think that's the only way I'll ever get what you mean when you keep saying "Invoke proper run method... No longer in scope... top level class" etc
    Ps; please don't take me the wrong way! It's just I don't understand when you used these terms which I've not come across before especially when talking about this subject which is my first try at it :3
     
Thread Status:
Not open for further replies.

Share This Page