Problem with getTargetBlock

Discussion in 'Spigot Plugin Development' started by Xuho, Jun 4, 2017.

  1. Code (Text):
    Player p = e.getPlayer();
    Block b = p.getTargetBlock(null, 10);
    It says the method getTargetBlock is ambigous for the type Player
    (PlayerInteractEvent)
     
  2. cast the first argument to a HashMap
     
  3. Not a hashmap but a hashset. (Or did it change in minecraft 1.12?)
    An explanation of this: there are multiple methods to get the target block. One takes in a hashset and a integer (it is deprecated) and on takes in a set and an integer. Normally this would be no problem, because varaibles are typed. When you give null for the first argument however, no type is known. Null could be any type. You could fix this by casting it to a set or hashset (I would use set because hashset is deprecated) or you could make a variable first (declare a type) and set it to a null pointer. This means that java knows the type for the variable, but it still equals null. In you case i would chose for the first option (casting) because it is only one line instead of two and there is no need for that variable to exist for any other reason.
     
  4. Or you can just use BlockIterator. This way you can control the range.


    Sent from my iPhone using Tapatalk
     
  5. Try this
    Code (Text):
    Block target = p.getTargetBlock((HashSet<Byte>) null, range);
     
  6. Personally, I'd use this.
    Code (Text):
        public final Block getTargetBlock(Player player, int range) {
            BlockIterator iter = new BlockIterator(player, range);
            Block lastBlock = iter.next();
            while (iter.hasNext()) {
                lastBlock = iter.next();
                if (lastBlock.getType() == Material.AIR) {
                    continue;
                }
                break;
            }
            return lastBlock;
        }
     
  7. I wouldn't, honestly. I'm pretty sure Player#getTargetBlock does it via NMS, meaning there's no need to convert between Bukkit API and NMS. Now, it isn't a lot 'heavier' or 'slower' to do it that way, but in case you'd be calling that very frequently, I wouldn't do it that way.
    Why? As an example, Block.getType() doesn't just return a cached field, but goes the following path.
    Code (Java):
    CraftBlock.class:
    @Override
        public Material getType() {
            return Material.getMaterial(this.getTypeId());
        }
     
        @Deprecated
        @Override
        public int getTypeId() {
            return CraftMagicNumbers.getId(this.chunk.getHandle().getBlockData(new BlockPosition(this.x, this.y, this.z)).getBlock());
        }

    --
    CraftMagicNumbers.class:
    @Deprecated
        public static int getId(final net.minecraft.server.v1_11_R1.Block block) {
            return net.minecraft.server.v1_11_R1.Block.getId(block);
        }

    net.minecraft.server.Block.class:
    public static int getId(final Block block) {
            return Block.REGISTRY.a(block);
        }

    RegistryBlocks.class: (Block.REGISTRY)
    @Override
        public int a(final V v) {
            final int a = super.a(v);
            return (a == -1) ? super.a(this.e) : a; // Registry lookups; fetching id.
        }

    --
    CraftChunk - getHandle():
    public net.minecraft.server.v1_11_R1.Chunk getHandle() {
            net.minecraft.server.v1_11_R1.Chunk c = this.weakChunk.get();
            if (c == null) {
                c = this.worldServer.getChunkAt(this.x, this.z);
                this.weakChunk = new WeakReference<net.minecraft.server.v1_11_R1.Chunk>(c);
            }
            return c;
        }

    --
    net.minecraft.server.Chunk.class:
    // vvv - this is called
    public final IBlockData getBlockData(final BlockPosition pos) {
            return this.getBlockData(pos.getX(), pos.getY(), pos.getZ());
        }
     
        public final IBlockData getBlockData(final int x, final int y, final int z) {
            final int i = y >> 4;
            if (y >= 0 && i < this.sections.length && this.sections[i] != null) {
                return this.sections[i].blockIds.a((y & 0xF) << 8 | (z & 0xF) << 4 | (x & 0xF));
            }
            return Blocks.AIR.getBlockData();
        }

    // blockIds is an instance of DataPaletteBlock class
    DataPaletteBlock.class:
    protected IBlockData a(final int i) {
            final IBlockData iblockdata = this.c.a(this.b.a(i));
            return (iblockdata == null) ? DataPaletteBlock.a : iblockdata;
        }
    // .. where 'this.b' an instance of DataBits class...
    DataBits.class:
    public int a(final int i) {
            final int j = i * this.b;
            final int k = j / 64;
            final int l = ((i + 1) * this.b - 1) / 64;
            final int i2 = j % 64;
            if (k == l) {
                return (int)(this.a[k] >>> i2 & this.c);
            }
            final int j2 = 64 - i2;
            return (int)((this.a[k] >>> i2 | this.a[l] << j2) & this.c);
        }
    // And 'this.c' in DataPaletteBlock is an instance of DataPaletteLinear
    DataPaletteLinear.class:
    @Nullable
        @Override
        public IBlockData a(final int n) {
            if (n >= 0 && n < this.d) {
                return this.a[n]; // Finally returning the IBlockData..
            }
            return null;
        }
    .. which is, in my opinion, pretty much overhead just for getting the Material type. And since all of this is called twice for each block in BlockIterator iteration (for both Block and its type).. eh, sure.

    I do admit I'm not sure what the Player getTargetBlock does under the hood, could check that honestly, but I assume it'd do something else.
     
  8. Sorry my fault :). Haven't checked it.
     
  9. thanks you so much guys, this help me a lot.