Cannot measure distance between locations - Error

Discussion in 'Plugin Development' started by KingFaris11, Feb 24, 2014.

Thread Status:
Not open for further replies.
  1. Hi, I've made a CTF plugin but I keep getting the error:
    EventListener.java:461
    Code:
    if (this.getPlugin().getGame().healingBase.distanceSquared(player.getLocation()) <= this.getPlugin().getSettings().getHealingRadius()) {
    
    I know this means that the worlds must be the same, but as you can see, it says "Distance between Colonies and Colonies"... they're the same world!
    On Line 460 I have made a check:
    Code:
    if (this.getPlugin().getGame().healingBase.getWorld().getName().equals(player.getLocation().getWorld().getName())) {
    ... I really need this, owner of MeepCraft, a big server network, has been waiting 2 days...

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 6, 2016
  2. How are you getting both locations? from config, or from any other method?
     
  3. One from a player, one from a method (that is loaded from a config). It loads fine, both locations aren't null...
     
  4. Offline

    Windy Day

    Try using location.distance(Location); ?
     
  5. All distance() does is use distanceSquared() but Maths.sqrt to square root it. It's just using up more memory/performance inefficient.
     
  6. Offline

    L33m4n123

    Is Colonies a custom class?
     
  7. No, a world name.
     
  8. Offline

    Jnorr44

    One of the values in the location may not be a number, which may be an issue.
     
  9. Values in the location, as in...? Because I know the 2 locations are both valid locations, when I use player.teleport(<location>) it teleports them to the correct location, except this is saying the worlds are different. This may be a 1.7.2 R0.3 bug... I'm not sure if I should tag TnT?
     
  10. Offline

    Jnorr44

    Try printing out the location, it may contain a number such as Float.NaN as pitch or yaw, which may be throwing an error.
     
    calebbfmv likes this.
  11. I will try that but I doubt it because when I use player.teleport(healingLoc) it teleports them to the correct place...
     
  12. Offline

    desht

    Your exception is thrown from Location, line 448, which reads:
    Code:
    else if (o.getWorld() != getWorld()) {
                throw new IllegalArgumentException("Cannot measure distance between " + getWorld().getName() + " and " + o.getWorld().getName());
            }
    
    So your worlds really aren't the same here; even though the two World objects both appear to have the name "Colonies" (as you mentioned, you checked the names), they're different objects.

    So the big question is, how are you creating the healingLoc location? Can you post that code?
     
  13. First bit of my onEnable():
    Code:
    plainScoreboard = this.getServer().getScoreboardManager().getNewScoreboard();
    this.game = new Game();
    this.settings = new Settings();
    this.loadConfiguration();
    
    In my loadConfiguration method, loaded 3rd thing in the onEnable() method.
    Code:
        this.getConfig().addDefault("Game world name", "ctf");
        this.getConfig().addDefault("Healing base", Utils.convertLocationToString(this.getServer().getWorlds().get(0).getSpawnLocation(), false));
        this.getConfig().options().copyDefaults(true);
        this.saveConfig();
     
        this.settings.setGameWorld(this.getConfig().getString("Game world name", "ctf"));
        World gameWorld = this.getServer().getWorld(this.settings.getGameWorld());
        if (gameWorld == null) gameWorld = this.getServer().createWorld(WorldCreator.name(this.settings.getGameWorld()));
        this.game.healingBase = Utils.convertStringToLocation(this.getConfig().getString("Healing base"), false);
    
    Utils.convertLocatonToString():
    Code:
    public static String convertLocationToString(Location location, boolean containsYawAndPitch) {
        if (location == null) return "world " + 0D + " " + 50D + " " + 0D + (containsYawAndPitch ? " " + 0F + " " + 0F : "");
        String strLoc = (location.getWorld() != null ? location.getWorld().getName() : "world") + " ";
        strLoc += location.getX() + " " + location.getY() + " " + location.getZ();
        if (containsYawAndPitch) strLoc += " " + location.getPitch() + " " + location.getYaw();
        return strLoc;
    }
    
    Utils.convertStringToLocation():
    Code:
    public static Location convertStringToLocation(String strLocation, boolean containsYawAndPitch) {
        if (strLocation == null) return null;
        try {
            if (strLocation.contains(" ")) {
                String[] locSplit = strLocation.split(" ");
                World w = Bukkit.getWorld(locSplit[0]);
                if (w == null) w = Bukkit.createWorld(WorldCreator.name(locSplit[0]));
                double x = Double.parseDouble(locSplit[1]), y = Double.parseDouble(locSplit[2]), z = Double.parseDouble(locSplit[3]);
                float yaw = 0F, pitch = 0F;
                if (containsYawAndPitch) {
                    yaw = Float.parseFloat(locSplit[4]);
                    pitch = Float.parseFloat(locSplit[5]);
                }
                return new Location(w, x, y, z, yaw, pitch);
            }
        } catch (Exception ex) {
        }
        return null;
    }
    
    The configuration has been set up with the correct values, Game world name being "Colonies" and the world name of the location being "Colonies" too.
     
  14. Offline

    desht

    Well, first thing: adding a catch (Exception ex) just to suppress stack traces is a terrible idea; ignoring exceptions like that just moves them to some other part of your code where it will be much harder to track them down.

    Next thing I'd suggest is adding a load of debug statements to your various methods, in particular convertStringToLocation() - dump out the values of the parameters it gets, all your intermediate calculations in there, and the returned World object (not just its name, the whole object).

    Better yet, dump out the UUID of your returned World versus the UUID of the player's World - I bet they're not the same. I suspect getWorld(locSplit[0]) may be returning null, and you're creating another world via Bukkit.createWorld() which happens to have the same name.
     
    bobacadodl and NathanWolf like this.
  15. Weird... I printed the UUID of both and they returned the same thing... Although I have a temporary fix, I could just remove the PlayerTeleportEvent listening method because if players do teleport to the flag place, that's cheating. :p They must move before claiming it. I think this works fine in the PlayerMoveEvent, the error is only thrown in PlayerTeleportEvent when they change worlds. I'll update this if it works without PlayerTeleportEvent...
     
  16. Offline

    NathanWolf

    Hm- it is unfortunate that Bukkit is using == there instead of .equals, since CraftWorld implements .equals as a UUID comparator.

    This means you have to have the *exact* same instance of the World object for that error to not occur.

    I concur with desht, I wonder where or how you are getting that second World reference- you are not calling createWorld, are you? Or are you somehow deserializing a World object direct from a config file into a new instance (versus using Bukkit's Location serialization, which would store the World as a String)?

    I guess one work-around you could do is like

    location.setWorld(server.getWorld(location.getWorld().getName());

    for both locations... that feels really weird to have to do that, but it should work since it'll make both locations use the same World instance from the Server's list of worlds.
     
  17. I am calling createWorld if the world is equal to null, meaning it would load the world if it exists, if not, it creates it. I'm not deserializing anything :p Also, the way I saved it was by setting the location to the player's location, then saving it to the config using my convertLocationToString() method... I did this to debug it:
    Code:
    for (World world : this.getServer().getWorlds()) {
        this.getServer().getConsoleSender().sendMessage(ChatColor.GOLD + "World: " + world.getName());
    }
    
    It printed out the correct worlds with no duplicates;
    This is still very weird... Although, I haven't tried it on the actual MeepCraft server, I'll send it to the owner when he's online, if he's online. I haven't got anything set up, including the locations or config on my server so the debug wasn't really much of a debug.
     
  18. Offline

    NathanWolf

    Oops, sorry, I missed that you already shared your location conversion methods... well jeez yah I really don't know then!

    Maybe have it debug print when it has to create a new world, since that should theoretically never happen? But maybe it's something weird like the worlds aren't loaded yet in onEnable.. ?
     
  19. Offline

    desht

    So, what happens if your method runs before the world in question is loaded? You'll end up creating a new world of that name, and then later on the original world will be loaded, and you end up with two different worlds with the same name, and confusion follows. Which, I suspect, is why the distanceSquared() method in Location is seeing two different World objects, even though they appear to be the same.
     
  20. Bukkit never loads any other worlds except the main world. To load another world you must use createWorld with the world name. If the world folder exists, it is loaded, if it does not, it is created. So it can't be loaded twice unless.... Unless the owner has Multiverse-Core...I will ask him.

    I just realised I could compare UIDs as they are the same, so it MUST be the same world.

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

    Sagacious_Zed Bukkit Docs

    The weird thing here is that there are two world objects with the same Data. However, there should only be one object with the particular data. This is only an indication of something else going on.
     
  22. I'm getting this:
    1. [16:48:34 INFO]: Player world UID: 5d8a1eba-2685-4838-83ba-5013b2c0d2fd
    2. [16:48:34 INFO]: Healing base UID: 5d8a1eba-2685-4838-83ba-5013b2c0d2fd

    This is weird... it's saying the worlds ARE the same yet it's still throwing that error. This is called in the PlayerMoveEvent and the PlayerTeleportEvent, even when removing from the PlayerTeleportEvent it doesn't work...
     
  23. Offline

    Sagacious_Zed Bukkit Docs

    The error is being thrown because it is not the same object, it has nothing to do with value equality between the two objects.
     
  24. But aren't all World objects the same if they have the same UID/world name? I mean, they all return the same World and so return the same World instance loaded from getWorlds()?

    Bump

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 6, 2016
  25. convert the location objects to vectors and then measure the distance? (as a temporary fix)
     
  26. Offline

    desht

    Clearly not, or you wouldn't be getting that exception. You have two different World objects (i.e. the object references are different) which point to the same actual World (hence the name and UUID match). What we're trying to figure out here is why that's happening, and I believe it's because you're creating a new world if your convertLocationToString() method can't find the world. That approach simply won't work in a multi-world situation where the world does exist, but just hasn't been loaded yet - you need to adjust your code to work with WorldLoadEvent/WorldUnloadEvent.

    (By the way, the world UUID is taken from the uid.dat file in the world's directory on disk - so if your two World objects have the same name, they'll have the same UUID too - but that isn't what Bukkit is checking for - it expects to see the exact same World object in the two Location objects being measured).
     
  27. Offline

    RawCode

    do you have any problems with pifagor teorem?

    what about extracting coords and parsing distance on your own?
     
  28. Offline

    NathanWolf

    I gave a work-around earlier that should work (lookup the Locations' Worlds by name to ensure they are the same object)

    Though I think we are all still eager to know why this isn't working, it's a bit mysterious.

    I am dubious that you need to be calling createWorld at all here. I persist locations without doing that and have never had an issue.

    Another workaround would be to use asVector and then just use the Vector class distance methods. This is akin to what RawCode was suggesting, though you don't need to really do the math yourself.

    The downside there is that Bukkit has that check for a reason, comparing locations in two different worlds makes no sense :) it's a good safety check to have.
     
  29. I removed the "Bukkit.createWorld()" and other world loading methods and made him use Multiverse-Core and use /mv import <world> etc. and it still gives that error. I'm still believing this is a bukkit-side bug as it really makes no sense in my opinion. All checks have been done...

    Never heard of "pifagor teorem". I think you mean "Pythagoras theorem"... you spelt it terribly wrong.
    Yes, I know pythagoras's theorem very well in Maths-terms but I have no idea how to implement it into x, y and z coordinates for Minecraft. I prefer relying on Bukkit's API rather than making my own code, easiest way and more stable as Bukkit would update. If anyone else could already type up a ready-made location distance thingy majiggy, I'd be VERY grateful.
     
  30. Offline

    NathanWolf

    Um... Again, I gave you the Bukkit API distance method already- did you mute me or something? :) do I type too much and your eyes gloss over?

    Lol, sorry, anyway Vector has the method you're looking for, or use the other work around I mentioned twice.
     
    bobacadodl likes this.
Thread Status:
Not open for further replies.

Share This Page