/** * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. * Copyright (C) 2015 dmulloy2 * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ package com.comphenix.protocol.wrappers; import java.lang.reflect.Constructor; import org.bukkit.Location; import org.bukkit.World; import com.comphenix.protocol.PacketType; import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.utility.MinecraftReflection; /** * Represents a single block change. * * @author dmulloy2 */ public class MultiBlockChangeInfo { private static Constructor constructor; private static Class nmsClass = MinecraftReflection.getMultiBlockChangeInfoClass(); private short location; private WrappedBlockData data; private ChunkCoordIntPair chunk; public MultiBlockChangeInfo(short location, WrappedBlockData data, ChunkCoordIntPair chunk) { this.location = location; this.data = data; this.chunk = chunk; } public MultiBlockChangeInfo(Location location, WrappedBlockData data) { this.data = data; this.chunk = new ChunkCoordIntPair(location.getBlockX() >> 4, location.getBlockZ() >> 4); this.setLocation(location); } /** * Returns this block change's absolute Location in a given World. * * @param world World for the location * @return This block change's absolute Location */ public Location getLocation(World world) { return new Location(world, getAbsoluteX(), getY(), getAbsoluteZ()); } /** * Sets this block change's absolute Location. * * @param location This block change's new location */ public void setLocation(Location location) { setLocation(location.getBlockX(), location.getBlockY(), location.getBlockZ()); } /** * Sets this block change's absolute coordinates. * * @param x X coordinate * @param y Y coordinate * @param z Z coordinate */ public void setLocation(int x, int y, int z) { x = x & 15; z = z & 15; this.location = (short) (x << 12 | z << 8 | y); } /** * Gets this block change's relative x coordinate. * * @return Relative X coordinate */ public int getX() { return location >> 12 & 15; } /** * Gets this block change's absolute x coordinate. * * @return Absolute X coordinate */ public int getAbsoluteX() { return (chunk.getChunkX() << 4) + getX(); } /** * Sets this block change's absolute x coordinate. * * @param x New x coordinate */ public void setX(int x) { setLocation(x, getY(), getZ()); } /** * Gets this block change's y coordinate. * * @return Y coordinate */ public int getY() { return location & 255; } /** * Sets this block change's y coordinate * * @param y New y coordinate */ public void setY(int y) { setLocation(getX(), y, getZ()); } /** * Gets this block change's relative z coordinate. * * @return Relative Z coordinate */ public int getZ() { return location >> 8 & 15; } /** * Gets this block change's absolute z coordinate. * * @return Absolute Z coordinate */ public int getAbsoluteZ() { return (chunk.getChunkZ() << 4) + getZ(); } /** * Sets this block change's relative z coordinate. * * @param z New z coordinate */ public void setZ(int z) { setLocation(getX(), getY(), z); } /** * Gets this block change's block data. * * @return The block data */ public WrappedBlockData getData() { return data; } /** * Sets this block change's block data. * * @param data New block data */ public void setData(WrappedBlockData data) { this.data = data; } /** * Gets the chunk this block change occured in. * * @return The chunk */ public ChunkCoordIntPair getChunk() { return chunk; } public static EquivalentConverter getConverter(final ChunkCoordIntPair chunk) { return new EquivalentConverter() { @Override public MultiBlockChangeInfo getSpecific(Object generic) { StructureModifier modifier = new StructureModifier<>(generic.getClass(), null, false).withTarget(generic); StructureModifier shorts = modifier.withType(short.class); short location = shorts.read(0); StructureModifier dataModifier = modifier.withType(MinecraftReflection.getIBlockDataClass(), BukkitConverters.getWrappedBlockDataConverter()); WrappedBlockData data = dataModifier.read(0); return new MultiBlockChangeInfo(location, data, chunk); } @Override public Object getGeneric(MultiBlockChangeInfo specific) { try { if (constructor == null) { constructor = nmsClass.getConstructor( PacketType.Play.Server.MULTI_BLOCK_CHANGE.getPacketClass(), short.class, MinecraftReflection.getIBlockDataClass() ); } return constructor.newInstance( null, specific.location, BukkitConverters.getWrappedBlockDataConverter().getGeneric(specific.data) ); } catch (Throwable ex) { throw new RuntimeException("Failed to construct MultiBlockChangeInfo instance.", ex); } } @Override public Class getSpecificType() { return MultiBlockChangeInfo.class; } }; } }