Package com.comphenix.protocol.events

Source Code of com.comphenix.protocol.events.PacketContainer$ItemStackArrayConverter

/*
*  ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
*  Copyright (C) 2012 Kristian S. Stangeland
*
*  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.events;

import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import net.minecraft.util.com.mojang.authlib.GameProfile;
import net.minecraft.util.io.netty.buffer.ByteBuf;
import net.minecraft.util.io.netty.buffer.UnpooledByteBufAllocator;

import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;

import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.injector.StructureCache;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.cloning.AggregateCloner;
import com.comphenix.protocol.reflect.cloning.BukkitCloner;
import com.comphenix.protocol.reflect.cloning.Cloner;
import com.comphenix.protocol.reflect.cloning.CollectionCloner;
import com.comphenix.protocol.reflect.cloning.FieldCloner;
import com.comphenix.protocol.reflect.cloning.ImmutableDetector;
import com.comphenix.protocol.reflect.cloning.SerializableCloner;
import com.comphenix.protocol.reflect.cloning.AggregateCloner.BuilderParameters;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.StreamSerializer;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.ChunkCoordIntPair;
import com.comphenix.protocol.wrappers.ChunkPosition;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.EnumWrappers.Difficulty;
import com.comphenix.protocol.wrappers.WrappedAttribute;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedServerPing;
import com.comphenix.protocol.wrappers.WrappedStatistic;
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import com.comphenix.protocol.wrappers.EnumWrappers.ChatVisibility;
import com.comphenix.protocol.wrappers.EnumWrappers.ClientCommand;
import com.comphenix.protocol.wrappers.EnumWrappers.EntityUseAction;
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import com.comphenix.protocol.wrappers.nbt.NbtBase;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/**
* Represents a Minecraft packet indirectly.
*
* @author Kristian
*/
public class PacketContainer implements Serializable {
  private static final long serialVersionUID = 3;
 
  protected PacketType type;
  protected transient Object handle;

  // Current structure modifier
  protected transient StructureModifier<Object> structureModifier;

  // Support for serialization
  private static ConcurrentMap<Class<?>, Method> writeMethods = Maps.newConcurrentMap();
  private static ConcurrentMap<Class<?>, Method> readMethods = Maps.newConcurrentMap();
 
  // Used to clone packets
  private static final AggregateCloner DEEP_CLONER = AggregateCloner.newBuilder().
      instanceProvider(DefaultInstances.DEFAULT).
      andThen(BukkitCloner.class).
      andThen(ImmutableDetector.class).
      andThen(CollectionCloner.class).
      andThen(getSpecializedDeepClonerFactory()).
      build();
 
  private static final AggregateCloner SHALLOW_CLONER = AggregateCloner.newBuilder().
      instanceProvider(DefaultInstances.DEFAULT).
      andThen(new Function<BuilderParameters, Cloner>() {
            @Override
            public Cloner apply(@Nullable BuilderParameters param) {
              if (param == null)
                throw new IllegalArgumentException("Cannot be NULL.");
             
              return new FieldCloner(param.getAggregateCloner(), param.getInstanceProvider()) {{
                // Use a default writer with no concept of cloning
                writer = new ObjectWriter();
              }};
            }
          }).
      build();
 
  // Packets that cannot be cloned by our default deep cloner
  private static final Set<PacketType> CLONING_UNSUPPORTED = Sets.newHashSet(
    PacketType.Play.Server.UPDATE_ATTRIBUTES, PacketType.Status.Server.OUT_SERVER_INFO);
 
  /**
   * Creates a packet container for a new packet.
   * <p>
   * Deprecated: Use {@link #PacketContainer(PacketType)} instead.
   * @param id - ID of the packet to create.
   */
  @Deprecated
  public PacketContainer(int id) {
    this(PacketType.findLegacy(id), StructureCache.newPacket(PacketType.findLegacy(id)));
  }
 
  /**
   * Creates a packet container for an existing packet.
   * <p>
   * Deprecated: Use {@link #PacketContainer(PacketType, Object))} instead.
   * @param id - ID of the given packet.
   * @param handle - contained packet.
   */
  @Deprecated
  public PacketContainer(int id, Object handle) {
    this(PacketType.findLegacy(id), handle);
  }
 
  /**
   * Creates a packet container for an existing packet.
   * <p>
   * Deprecated: Use {@link #PacketContainer(PacketType, Object, StructureModifier))} instead.
   * @param id - ID of the given packet.
   * @param handle - contained packet.
   * @param structure - structure modifier.
   */
  @Deprecated
  public PacketContainer(int id, Object handle, StructureModifier<Object> structure) {
    this(PacketType.findLegacy(id), handle, structure);
  }
 
  /**
   * Creates a packet container for a new packet.
   * @param type - the type of the packet to create.
   */
  public PacketContainer(PacketType type) {
    this(type, StructureCache.newPacket(type));
  }
 
  /**
   * Creates a packet container for an existing packet.
   * @param id - ID of the given packet.
   * @param handle - contained packet.
   */
  public PacketContainer(PacketType type, Object handle) {
    this(type, handle, StructureCache.getStructure(type).withTarget(handle));
  }
 
  /**
   * Creates a packet container for an existing packet.
   * @param id - ID of the given packet.
   * @param handle - contained packet.
   * @param structure - structure modifier.
   */
  public PacketContainer(PacketType type, Object handle, StructureModifier<Object> structure) {
    if (handle == null)
      throw new IllegalArgumentException("handle cannot be null.");
    if (type == null)
      throw new IllegalArgumentException("type cannot be null.");
   
    this.type = type;
    this.handle = handle;
    this.structureModifier = structure;
  }
 
  /**
   * Construct a new packet container from a given handle.
   * @param packet - the NMS packet.
   * @return The packet container.
   */
  public static PacketContainer fromPacket(Object packet) {
    PacketType type = PacketType.fromClass(packet.getClass());
    return new PacketContainer(type, packet);
  }
 
  /**
   * For serialization.
   */
  protected PacketContainer() {
  }
 
  /**
   * Retrieves the underlying Minecraft packet.
   * @return Underlying Minecraft packet.
   */
  public Object getHandle() {
    return handle;
  }
 
  /**
   * Retrieves the generic structure modifier for this packet.
   * @return Structure modifier.
   */
  public StructureModifier<Object> getModifier() {
    return structureModifier;
  }
 
  /**
   * Retrieves a read/write structure for every field with the given type.
   * @param primitiveType - the type to find.
   * @return A modifier for this specific type.
   */
  public <T> StructureModifier<T> getSpecificModifier(Class<T> primitiveType) {
    return structureModifier.withType(primitiveType);
  }
 
  /**
   * Retrieves a read/write structure for every byte field.
   * @return A modifier for every byte field.
   */
  public StructureModifier<Byte> getBytes() {
    return structureModifier.withType(byte.class);
  }
 
  /**
   * Retrieves a read/write structure for every boolean field.
   * @return A modifier for every boolean field.
   */
  public StructureModifier<Boolean> getBooleans() {
    return structureModifier.withType(boolean.class);
  }
 
  /**
   * Retrieves a read/write structure for every short field.
   * @return A modifier for every short field.
   */
  public StructureModifier<Short> getShorts() {
    return structureModifier.withType(short.class);
  }
 
  /**
   * Retrieves a read/write structure for every integer field.
   * @return A modifier for every integer field.
   */
  public StructureModifier<Integer> getIntegers() {
    return structureModifier.withType(int.class);
  }
  /**
   * Retrieves a read/write structure for every long field.
   * @return A modifier for every long field.
   */
  public StructureModifier<Long> getLongs() {
    return structureModifier.withType(long.class);
  }
 
  /**
   * Retrieves a read/write structure for every float field.
   * @return A modifier for every float field.
   */
  public StructureModifier<Float> getFloat() {
    return structureModifier.withType(float.class);
  }
 
  /**
   * Retrieves a read/write structure for every double field.
   * @return A modifier for every double field.
   */
  public StructureModifier<Double> getDoubles() {
    return structureModifier.withType(double.class);
  }
 
  /**
   * Retrieves a read/write structure for every String field.
   * @return A modifier for every String field.
   */
  public StructureModifier<String> getStrings() {
    return structureModifier.withType(String.class);
  }
 
  /**
   * Retrieves a read/write structure for every String array field.
   * @return A modifier for every String array field.
   */
  public StructureModifier<String[]> getStringArrays() {
    return structureModifier.withType(String[].class);
  }
 
  /**
   * Retrieves a read/write structure for every byte array field.
   * @return A modifier for every byte array field.
   */
  public StructureModifier<byte[]> getByteArrays() {
    return structureModifier.withType(byte[].class);
  }
 
  /**
   * Retrieve a serializer for reading and writing ItemStacks stored in a byte array.
   * @return A instance of the serializer.
   */
  public StreamSerializer getByteArraySerializer() {
    return new StreamSerializer();
  }

  /**
   * Retrieves a read/write structure for every int array field.
   * @return A modifier for every int array field.
   */
  public StructureModifier<int[]> getIntegerArrays() {
    return structureModifier.withType(int[].class);
  }
 
  /**
   * Retrieves a read/write structure for ItemStack.
   * <p>
   * This modifier will automatically marshall between the Bukkit ItemStack and the
   * internal Minecraft ItemStack.
   * @return A modifier for ItemStack fields.
   */
  public StructureModifier<ItemStack> getItemModifier() {
    // Convert to and from the Bukkit wrapper
    return structureModifier.<ItemStack>withType(
        MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter());
  }
 
  /**
   * Retrieves a read/write structure for arrays of ItemStacks.
   * <p>
   * This modifier will automatically marshall between the Bukkit ItemStack and the
   * internal Minecraft ItemStack.
   * @return A modifier for ItemStack array fields.
   */
  public StructureModifier<ItemStack[]> getItemArrayModifier() {   
    // Convert to and from the Bukkit wrapper
    return structureModifier.<ItemStack[]>withType(
        MinecraftReflection.getItemStackArrayClass(),
        BukkitConverters.getIgnoreNull(new ItemStackArrayConverter()));
  }
 
  /**
   * Retrieve a read/write structure for maps of statistics.
   * <p>
   * Note that you must write back the changed map to persist it.
   * @return A modifier for maps of statistics.
   */
  public StructureModifier<Map<WrappedStatistic, Integer>> getStatisticMaps() {
    return structureModifier.withType(Map.class,
      BukkitConverters.<WrappedStatistic, Integer>getMapConverter(
        MinecraftReflection.getStatisticClass(),
        BukkitConverters.getWrappedStatisticConverter()
      )
    );
  }
 
  /**
   * Retrieves a read/write structure for the world type enum.
   * <p>
   * This modifier will automatically marshall between the Bukkit world type and the
   * internal Minecraft world type.
   * @return A modifier for world type fields.
   */
  public StructureModifier<WorldType> getWorldTypeModifier() {
    // Convert to and from the Bukkit wrapper
    return structureModifier.<WorldType>withType(
        MinecraftReflection.getWorldTypeClass(),
        BukkitConverters.getWorldTypeConverter());
  }
 
  /**
   * Retrieves a read/write structure for data watchers.
   * @return A modifier for data watchers.
   */
  public StructureModifier<WrappedDataWatcher> getDataWatcherModifier() {
    // Convert to and from the Bukkit wrapper
    return structureModifier.<WrappedDataWatcher>withType(
        MinecraftReflection.getDataWatcherClass(),
        BukkitConverters.getDataWatcherConverter());
  }
 
  /**
   * Retrieves a read/write structure for entity objects.
   * <p>
   * Note that entities are transmitted by integer ID, and the type may not be enough
   * to distinguish between entities and other values. Thus, this structure modifier
   * MAY return null or invalid entities for certain fields. Using the correct index
   * is essential.
   *
   * @param world - the world each entity is currently occupying.
   * @return A modifier entity types.
   */
  public StructureModifier<Entity> getEntityModifier(@Nonnull World world) {
    Preconditions.checkNotNull(world, "world cannot be NULL.");
    // Convert to and from the Bukkit wrapper
    return structureModifier.<Entity>withType(
        int.class, BukkitConverters.getEntityConverter(world));
  }
 
  /**
   * Retrieves a read/write structure for entity objects.
   * <p>
   * Note that entities are transmitted by integer ID, and the type may not be enough
   * to distinguish between entities and other values. Thus, this structure modifier
   * MAY return null or invalid entities for certain fields. Using the correct index
   * is essential.
   *
   * @param event - the original packet event.
   * @return A modifier entity types.
   */
  public StructureModifier<Entity> getEntityModifier(@Nonnull PacketEvent event) {
    Preconditions.checkNotNull(event, "event cannot be NULL.");
    return getEntityModifier(event.getPlayer().getWorld());
  }
 
  /**
   * Retrieves a read/write structure for chunk positions.
   * @return A modifier for a ChunkPosition.
   */
  public StructureModifier<ChunkPosition> getPositionModifier() {
    // Convert to and from the Bukkit wrapper
    return structureModifier.withType(
        MinecraftReflection.getChunkPositionClass(),
        ChunkPosition.getConverter());
  }
 
  /**
   * Retrieves a read/write structure for wrapped ChunkCoordIntPairs.
   * @return A modifier for ChunkCoordIntPair.
   */
  public StructureModifier<ChunkCoordIntPair> getChunkCoordIntPairs() {
    // Allow access to the NBT class in packet 130
    return structureModifier.withType(
        MinecraftReflection.getChunkCoordIntPair(),
        ChunkCoordIntPair.getConverter());
  }
 
  /**
   * Retrieves a read/write structure for NBT classes.
   * @return A modifier for NBT classes.
   */
  public StructureModifier<NbtBase<?>> getNbtModifier() {
    // Allow access to the NBT class in packet 130
    return structureModifier.withType(
        MinecraftReflection.getNBTBaseClass(),
        BukkitConverters.getNbtConverter());
  }
 
  /**
   * Retrieves a read/write structure for collections of attribute snapshots.
   * <p>
   * This modifier will automatically marshall between the visible ProtocolLib WrappedAttribute and the
   * internal Minecraft AttributeSnapshot.
   * @return A modifier for AttributeSnapshot collection fields.
   */
  public StructureModifier<List<WrappedAttribute>> getAttributeCollectionModifier() {
    // Convert to and from the ProtocolLib wrapper
    return structureModifier.withType(
      Collection.class,
      BukkitConverters.getListConverter(
          MinecraftReflection.getAttributeSnapshotClass(),
          BukkitConverters.getWrappedAttributeConverter())
    );
  }
 
  /**
   * Retrieves a read/write structure for collections of chunk positions.
   * <p>
   * This modifier will automatically marshall between the visible ProtocolLib ChunkPosition and the
   * internal Minecraft ChunkPosition.
   * @return A modifier for ChunkPosition list fields.
   */
  public StructureModifier<List<ChunkPosition>> getPositionCollectionModifier() {
    // Convert to and from the ProtocolLib wrapper
    return structureModifier.withType(
      Collection.class,
      BukkitConverters.getListConverter(
          MinecraftReflection.getChunkPositionClass(),
          ChunkPosition.getConverter())
    );
  }
 
  /**
   * Retrieves a read/write structure for collections of watchable objects.
   * <p>
   * This modifier will automatically marshall between the visible WrappedWatchableObject and the
   * internal Minecraft WatchableObject.
   * @return A modifier for watchable object list fields.
   */
  public StructureModifier<List<WrappedWatchableObject>> getWatchableCollectionModifier() {
    // Convert to and from the ProtocolLib wrapper
    return structureModifier.withType(
      Collection.class,
      BukkitConverters.getListConverter(
          MinecraftReflection.getWatchableObjectClass(),
          BukkitConverters.getWatchableObjectConverter())
    );
  }
 
 
  /**
   * Retrieves a read/write structure for block fields.
   * <p>
   * This modifier will automatically marshall between Material and the
   * internal Minecraft Block.
   * @return A modifier for GameProfile fields.
   */
  public StructureModifier<Material> getBlocks() {
    // Convert to and from the Bukkit wrapper
    return structureModifier.<Material>withType(
        MinecraftReflection.getBlockClass(), BukkitConverters.getBlockConverter());
  }
 
  /**
   * Retrieves a read/write structure for game profiles in Minecraft 1.7.2.
   * <p>
   * This modifier will automatically marshall between WrappedGameProfile and the
   * internal Minecraft GameProfile.
   * @return A modifier for GameProfile fields.
   */
  public StructureModifier<WrappedGameProfile> getGameProfiles() {
    // Convert to and from the Bukkit wrapper
    return structureModifier.<WrappedGameProfile>withType(
        GameProfile.class, BukkitConverters.getWrappedGameProfileConverter());
  }
 
  /**
   * Retrieves a read/write structure for chat components in Minecraft 1.7.2.
   * <p>
   * This modifier will automatically marshall between WrappedChatComponent and the
   * internal Minecraft GameProfile.
   * @return A modifier for ChatComponent fields.
   */
  public StructureModifier<WrappedChatComponent> getChatComponents() {
    // Convert to and from the Bukkit wrapper
    return structureModifier.<WrappedChatComponent>withType(
        MinecraftReflection.getIChatBaseComponentClass(), BukkitConverters.getWrappedChatComponentConverter());
  }
 
  /**
   * Retrieve a read/write structure for the ServerPing fields in the following packet: <br>
   * <ul>
   *   <li>{@link PacketType.Status.Server#OUT_SERVER_INFO}
   * </ul>
   * @return A modifier for ServerPing fields.
   */
  public StructureModifier<WrappedServerPing> getServerPings() {
    // Convert to and from the wrapper
    return structureModifier.<WrappedServerPing>withType(
        MinecraftReflection.getServerPingClass(), BukkitConverters.getWrappedServerPingConverter());
  }
 
  /**
   * Retrieve a read/write structure for the Protocol enum in 1.7.2.
   * @return A modifier for Protocol enum fields.
   */
  public StructureModifier<Protocol> getProtocols() {
    // Convert to and from the wrapper
    return structureModifier.<Protocol>withType(
        EnumWrappers.getProtocolClass(), EnumWrappers.getProtocolConverter());
  }
 
  /**
   * Retrieve a read/write structure for the ClientCommand enum in 1.7.2.
   * @return A modifier for ClientCommand enum fields.
   */
  public StructureModifier<ClientCommand> getClientCommands() {
    // Convert to and from the wrapper
    return structureModifier.<ClientCommand>withType(
        EnumWrappers.getClientCommandClass(), EnumWrappers.getClientCommandConverter());
  }

  /**
   * Retrieve a read/write structure for the ChatVisibility enum in 1.7.2.
   * @return A modifier for ChatVisibility enum fields.
   */
  public StructureModifier<ChatVisibility> getChatVisibilities() {
    // Convert to and from the wrapper
    return structureModifier.<ChatVisibility>withType(
        EnumWrappers.getChatVisibilityClass(), EnumWrappers.getChatVisibilityConverter());
  }
 
  /**
   * Retrieve a read/write structure for the Difficulty enum in 1.7.2.
   * @return A modifier for Difficulty enum fields.
   */
  public StructureModifier<Difficulty> getDifficulties() {
    // Convert to and from the wrapper
    return structureModifier.<Difficulty>withType(
        EnumWrappers.getDifficultyClass(), EnumWrappers.getDifficultyConverter());
  }
 
  /**
   * Retrieve a read/write structure for the EntityUse enum in 1.7.2.
   * @return A modifier for EntityUse enum fields.
   */
  public StructureModifier<EntityUseAction> getEntityUseActions() {
    // Convert to and from the wrapper
    return structureModifier.<EntityUseAction>withType(
        EnumWrappers.getEntityUseActionClass(), EnumWrappers.getEntityUseActionConverter());
  }
 
  /**
   * Retrieve a read/write structure for the NativeGameMode enum in 1.7.2.
   * @return A modifier for NativeGameMode enum fields.
   */
  public StructureModifier<NativeGameMode> getGameModes() {
    // Convert to and from the wrapper
    return structureModifier.<NativeGameMode>withType(
        EnumWrappers.getGameModeClass(), EnumWrappers.getGameModeConverter());
  }
 
  /**
   * Retrieves the ID of this packet.
   * <p>
   * Deprecated: Use {@link #getType()} instead.
   * @return Packet ID.
   */
  @Deprecated
  public int getID() {
    return type.getLegacyId();
  }
 
  /**
   * Retrieve the packet type of this packet.
   * @return The packet type.
   */
  public PacketType getType() {
    return type;
  }
 
  /**
   * Create a shallow copy of the current packet.
   * <p>
   * This merely writes the content of each field to the new class directly,
   * without performing any expensive copies.
   *
   * @return A shallow copy of the current packet.
   */
  public PacketContainer shallowClone() {
    Object clonedPacket = SHALLOW_CLONER.clone(getHandle());
    return new PacketContainer(getType(), clonedPacket);
  }
 
  /**
   * Create a deep copy of the current packet.
   * <p>
   * This will perform a full copy of the entire object tree, only skipping
   * known immutable objects and primitive types.
   * <p>
   * Note that the inflated buffers in packet 51 and 56 will be copied directly to save memory.
   *
   * @return A deep copy of the current packet.
   */
  public PacketContainer deepClone() {
    Object clonedPacket = null;
   
    // Fall back on the alternative (but slower) method of reading and writing back the packet
    if (CLONING_UNSUPPORTED.contains(type)) {
      clonedPacket = SerializableCloner.clone(this).getHandle();
    } else {
      clonedPacket = DEEP_CLONER.clone(getHandle());
    }
    return new PacketContainer(getType(), clonedPacket);
  }
   
  // To save space, we'll skip copying the inflated buffers in packet 51 and 56
  private static Function<BuilderParameters, Cloner> getSpecializedDeepClonerFactory() {
    // Look at what you've made me do Java, look at it!!
    return new Function<BuilderParameters, Cloner>() {
      @Override
      public Cloner apply(@Nullable BuilderParameters param) {
        return new FieldCloner(param.getAggregateCloner(), param.getInstanceProvider()) {{
          this.writer = new ObjectWriter() {
            protected void transformField(StructureModifier<Object> modifierSource,
                            StructureModifier<Object> modifierDest, int fieldIndex) {
              // No need to clone inflated buffers
              if (modifierSource.getField(fieldIndex).getName().startsWith("inflatedBuffer"))
                modifierDest.write(fieldIndex, modifierSource.read(fieldIndex));
              else
                defaultTransform(modifierSource, modifierDest, getDefaultCloner(), fieldIndex);
            };
          };
        }};
      }
    };
  }
 
  private void writeObject(ObjectOutputStream output) throws IOException {
      // Default serialization
    output.defaultWriteObject();

    // We'll take care of NULL packets as well
    output.writeBoolean(handle != null);

    try {
      if (MinecraftReflection.isUsingNetty()) {
        ByteBuf buffer = createPacketBuffer();
        MinecraftMethods.getPacketWriteByteBufMethod().invoke(handle, buffer);
       
        output.writeInt(buffer.readableBytes());
        buffer.readBytes(output, buffer.readableBytes());
       
      } else {
        // Call the write-method
        output.writeInt(-1);
        getMethodLazily(writeMethods, handle.getClass(), "write", DataOutput.class).
          invoke(handle, new DataOutputStream(output))
      }
   
    } catch (IllegalArgumentException e) {
      throw new IOException("Minecraft packet doesn't support DataOutputStream", e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException("Insufficient security privileges.", e);
    } catch (InvocationTargetException e) {
      throw new IOException("Could not serialize Minecraft packet.", e);
    }
  }

  private void readObject(ObjectInputStream input) throws ClassNotFoundException, IOException {
      // Default deserialization
    input.defaultReadObject();
   
    // Get structure modifier
    structureModifier = StructureCache.getStructure(type);

      // Don't read NULL packets
      if (input.readBoolean()) {
       
        // Create a default instance of the packet
        handle = StructureCache.newPacket(type);
       
      // Call the read method
      try {
        if (MinecraftReflection.isUsingNetty()) {
          ByteBuf buffer = createPacketBuffer();
          buffer.writeBytes(input, input.readInt());
         
          MinecraftMethods.getPacketReadByteBufMethod().invoke(handle, buffer);
        } else {
          if (input.readInt() != -1)
            throw new IllegalArgumentException("Cannot load a packet from 1.7.2 in 1.6.4.");
         
          getMethodLazily(readMethods, handle.getClass(), "read", DataInput.class).
            invoke(handle, new DataInputStream(input));
        }
      } catch (IllegalArgumentException e) {
        throw new IOException("Minecraft packet doesn't support DataInputStream", e);
      } catch (IllegalAccessException e) {
        throw new RuntimeException("Insufficient security privileges.", e);
      } catch (InvocationTargetException e) {
        throw new IOException("Could not deserialize Minecraft packet.", e);
      }
     
      // And we're done
      structureModifier = structureModifier.withTarget(handle);
      }
  }
 
  /**
   * Construct a new packet data serializer.
   * @return The packet data serializer.
   */
  private ByteBuf createPacketBuffer() {
    return MinecraftReflection.getPacketDataSerializer(UnpooledByteBufAllocator.DEFAULT.buffer());
  }
 
  /**
   * Retrieve the cached method concurrently.
   * @param lookup - a lazy lookup cache.
   * @param handleClass - class type of the current packet.
   * @param methodName - name of method to retrieve.
   * @param parameterClass - the one parameter type in the method.
   * @return Reflected method.
   */
  private Method getMethodLazily(ConcurrentMap<Class<?>, Method> lookup,
                   Class<?> handleClass, String methodName, Class<?> parameterClass) {
    Method method = lookup.get(handleClass);
   
    // Atomic operation
    if (method == null) {
      Method initialized = FuzzyReflection.fromClass(handleClass).getMethod(
              FuzzyMethodContract.newBuilder().
              parameterCount(1).
              parameterDerivedOf(parameterClass).
              returnTypeVoid().
              build());
      method = lookup.putIfAbsent(handleClass, initialized);
     
      // Use our version if we succeeded
      if (method == null) {
        method = initialized;
      }
    }
   
    return method;
  }
 
  /**
   * Represents an equivalent converter for ItemStack arrays.
   * @author Kristian
   */
  private static class ItemStackArrayConverter implements EquivalentConverter<ItemStack[]> {
    final EquivalentConverter<ItemStack> stackConverter = BukkitConverters.getItemStackConverter();
   
    public Object getGeneric(Class<?>genericType, ItemStack[] specific) {
      Class<?> nmsStack = MinecraftReflection.getItemStackClass();
      Object[] result = (Object[]) Array.newInstance(nmsStack, specific.length);
     
      // Unwrap every item
      for (int i = 0; i < result.length; i++) {
        result[i] = stackConverter.getGeneric(nmsStack, specific[i]);
      }
      return result;
    }
   
    @Override
    public ItemStack[] getSpecific(Object generic) {
      Object[] input = (Object[]) generic;
      ItemStack[] result = new ItemStack[input.length];
     
      // Add the wrapper
      for (int i = 0; i < result.length; i++) {
        result[i] = stackConverter.getSpecific(input[i]);
      }
      return result;
    }
   
    @Override
    public Class<ItemStack[]> getSpecificType() {
      return ItemStack[].class;
    }
  }
}
TOP

Related Classes of com.comphenix.protocol.events.PacketContainer$ItemStackArrayConverter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.