1.15.2 Compare same chunk, but its not the same.

Discussion in 'Spigot Plugin Development' started by LiveTeXon, Jan 20, 2020.

Thread Status:
Not open for further replies.
  1. Hey, i have an problem with my game. I cant show any code becurse the code is huge... but hopely someone can help me. So i have an Hashmap with an Chunk and a ChunkObject (ChunkObject is an Class with Chunk and User Infos) when i try to call if the chunk of the player location contains in the Hashmap java says false. But its 100% the same chunk. When i print the Chunk from Hash and Player its 100% the same chunk. But why java says that the chunks isnt the same?
     
  2. Can you show us the code were you save thr chunks to hashmap and compare it? You can either use thr code tag or copy & paste the code in pastebin/hastebin?
     
  3. Assuming org.bukkit.Chunk is the key of the HashMap, this behaviour is expected. org.bukkit.craftbukkit.CraftChunk does not implement the java.lang.Object#hashCode method.

    I wrote a small test command

    Code (Java):

    Player player = (Player) sender;

    int chunkX = Integer.parseInt(args[0]);
    int chunkZ = Integer.parseInt(args[1]);

    player.sendMessage("Hash of chunk (" + chunkX + ", " + chunkZ + ") is " + player.getWorld().getChunkAt(chunkX, chunkZ).hashCode());
     
    I then teleported 1000 blocks away from spawn, ran the command. Then teleported back to spawn, effectively unloading the chunk. I then teleported again and ran the hash command again.

    This is the result I got:

    [​IMG]

    The result is different, because the org.bukkit.craftbukkit.CraftChunk is now a completely different instance, thus returning a different hashCode.

    Replace your Chunk HashMap key with another DataType that implements java.lang.Object#hashCode. You can even write your own:

    Code (Java):

    class ChunkCoords {
        public final int x, z;

        @Override
        public int hashCode() {
            return Objects.hash(x, z);
        }

        // also implement equals and maybe a constructor
    }
     
     
    • Winner Winner x 2
  4. Either wrap the chunk in another object and implement a hashcode method that uses the x/y coordinates or (event better)
    dont have a hard linked object and just implement a new class with only the x and y coordinates or (even better) use a long to represent the two int coordinates.
    Just use a bit mask to pack them in a long a bit like this
    int x;
    int z;
    long key = (x & 0xFFFFFFFF) << 32 | (z & 0xFFFFFFFF);

    then unpack them again:
    long key;
    int x = (int) (key >> 32);
    int z = (int) key;
     

  5. But when i had print the Chunk with System.out.print(chunk.toString()); it was the same chunk
     
  6. The difference is that you were not working with the same Chunk object in the Virtual Machine. A HashMap does not look at the data inside the Object you use as a key, it only looks at the hashCode() and equals() methods. Both of these methods should be implemented, if you want to use a certain Data Type as a HashMap Key. CraftChunk does not implement any of these two methods, so it is not recommended to use a Chunk (The interface implemented by CraftChunk) as a HashMap Key.

    To demonstrate further why, let's say I made a small class like this:

    Code (Java):

    public class Message {
        private final String value;

        public Message(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }

        @Override
        public String toString() {
            return value;
        }
    }
     
    and then I created two instances of said class like this:

    Code (Java):

    Message a = new Message("Hello, World!");
    Message b = new Message("Hello, World!");
     
    These two objects contains the same data (The String "Hello, World!") but they are two completely different objects inside the Java virtual machine, therefore calling a == b will evaluate to false. Which indeed does happen:

    [​IMG]

    Calling toString() and hashCode() results in this:

    [​IMG]

    As you can see, toString() results in the same string, but the hashCode is completely different.

    The hashCode is what a HashMap internally uses to identify Keys, resulting in this:

    [​IMG]

    If I now implement the hashCode and equals method inside the Message class and rerun the tests, the result's will look different:

    Code (Java):

    public class Message {
        private final String value;

        public Message(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }

        @Override
        public String toString() {
            return value;
        }

        @Override
        public int hashCode() {
            return value.hashCode();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Message message = (Message) o;
            return Objects.equals(value, message.value);
        }
    }
     

    [​IMG]

    This is pretty much a simplified version of what is happening internally with your Chunk. You are storing a value inside the HashMap using a Chunk. Later that Chunk gets unloaded (or for Whatever reason, CraftBukkit creates a new instance of CraftChunk and returns that). Since CraftChunk does not implement hashCode, the hashCode function returns the memory address of the Object (in most cases). Your HashMap then cannot find a Value based on that hashCode.
     
    • Winner Winner x 2
Thread Status:
Not open for further replies.