Package com.comphenix.protocol.injector.packet

Source Code of com.comphenix.protocol.injector.packet.ProxyPacketInjector$PacketClassLookup

/*
*  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.injector.packet;

import java.io.DataInput;
import java.io.DataInputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import org.bukkit.entity.Player;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.NoOp;

import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.events.ListenerOptions;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.ListenerInvoker;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.MethodInfo;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.EnhancerFactory;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedIntHashMap;

/**
* This class is responsible for adding or removing proxy objects that intercepts received packets.
*
* @author Kristian
*/
class ProxyPacketInjector implements PacketInjector {
  public static final ReportType REPORT_CANNOT_FIND_READ_PACKET_METHOD = new ReportType("Cannot find read packet method for ID %s.");
  public static final ReportType REPORT_UNKNOWN_ORIGIN_FOR_PACKET = new ReportType("Timeout: Unknown origin %s for packet %s. Are you using GamePhase.LOGIN?");
 
  /**
   * Represents a way to update the packet ID to class lookup table.
   * @author Kristian
   */
  private static interface PacketClassLookup {
    public void setLookup(int packetID, Class<?> clazz);
  }
 
  private static class IntHashMapLookup implements PacketClassLookup {
    private WrappedIntHashMap intHashMap;
   
    public IntHashMapLookup() throws IllegalAccessException {
      initialize();
    }
   
    @Override
    public void setLookup(int packetID, Class<?> clazz) {
      intHashMap.put(packetID, clazz);
    }

    private void initialize() throws IllegalAccessException {
      if (intHashMap == null) {
        // We're looking for the first static field with a Minecraft-object. This should be a IntHashMap.
        Field intHashMapField = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true).
            getFieldByType("packetIdMap", MinecraftReflection.getIntHashMapClass());
       
        try {
          intHashMap = WrappedIntHashMap.fromHandle(
            FieldUtils.readField(intHashMapField, (Object) null, true));
        } catch (IllegalArgumentException e) {
          throw new RuntimeException("Minecraft is incompatible.", e);
        }
      }
    }
  }
 
  private static class ArrayLookup implements PacketClassLookup {
    private Class<?>[] array;
   
    public ArrayLookup() throws IllegalAccessException {
      initialize();
    }
   
    @Override
    public void setLookup(int packetID, Class<?> clazz) {
      array[packetID] = clazz;
    }

    private void initialize() throws IllegalAccessException {
      FuzzyReflection reflection = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass());
     
      // Is there a Class array with 256 elements instead?
      for (Field field : reflection.getFieldListByType(Class[].class)) {
        Class<?>[] test = (Class<?>[]) FieldUtils.readField(field, (Object)null);
       
        if (test.length == 256) {
          array = test;
          return;
        }
      }
      throw new IllegalArgumentException(
          "Unable to find an array with the type " + Class[].class +
          " in " + MinecraftReflection.getPacketClass());
    }
  }
 
  /**
   * Matches the readPacketData(DataInputStream) method in Packet.
   */
  private static FuzzyMethodContract READ_PACKET = FuzzyMethodContract.newBuilder().
      returnTypeVoid().
      parameterDerivedOf(DataInput.class).
      parameterCount(1).
      build();
 
  private static PacketClassLookup lookup;
 
  // The packet filter manager
  private ListenerInvoker manager;
 
  // Error reporter
  private ErrorReporter reporter;
 
  // Allows us to determine the sender
  private PlayerInjectionHandler playerInjection;
 
  // Share callback filter
  private CallbackFilter filter;
 
  // Determine if the read packet method was found
  private boolean readPacketIntercepted = false;
 
  public ProxyPacketInjector(ListenerInvoker manager, PlayerInjectionHandler playerInjection,
                 ErrorReporter reporter) throws FieldAccessException {
   
    this.manager = manager;
    this.playerInjection = playerInjection;
    this.reporter = reporter;
    initialize();
  }
 
  @Override
  public boolean isCancelled(Object packet) {
    return ReadPacketModifier.isCancelled(packet);
  }
 
  @Override
  public void setCancelled(Object packet, boolean cancelled) {
    if (cancelled) {
      ReadPacketModifier.setOverride(packet, null);
    } else {
      ReadPacketModifier.removeOverride(packet);
    }
  }
 
  private void initialize() throws FieldAccessException {
    if (lookup == null) {
      try {
        lookup = new IntHashMapLookup();
      } catch (Exception e1) {
       
        try {
          lookup = new ArrayLookup();
        } catch (Exception e2) {
          // Wow
          throw new FieldAccessException(e1.getMessage() + ". Workaround failed too.", e2);
        }
      }
     
      // Should work fine now
    }
  }
 
  @Override
  public void inputBuffersChanged(Set<PacketType> set) {
    // No need to do anything
  }
 
  @Override
  @SuppressWarnings({"rawtypes", "deprecation"})
  public boolean addPacketHandler(PacketType type, Set<ListenerOptions> options) {
    final int packetID = type.getLegacyId();
   
    if (hasPacketHandler(type))
      return false;
   
    Enhancer ex = EnhancerFactory.getInstance().createEnhancer();
   
    // Unfortunately, we can't easily distinguish between these two functions:
    //   * Object lookup(int par1)
    //   * Object removeObject(int par1)
   
    // So, we'll use the classMapToInt registry instead.
    Map<Integer, Class> overwritten = PacketRegistry.getOverwrittenPackets();
    Map<Integer, Class> previous = PacketRegistry.getPreviousPackets();
    Map<Class, Integer> registry = PacketRegistry.getPacketToID();
    Class old = PacketRegistry.getPacketClassFromType(type);
   
    // If this packet is not known
    if (old == null) {
      throw new IllegalStateException("Packet ID " + type + " is not a valid packet type in this version.");
    }
    // Check for previous injections
    if (Factory.class.isAssignableFrom(old)) {
      throw new IllegalStateException("Packet " + type + " has already been injected.");
    }
   
    if (filter == null) {
      readPacketIntercepted = false;
     
      filter = new CallbackFilter() {
        @Override
        public int accept(Method method) {
          // Skip methods defined in Object
          if (method.getDeclaringClass().equals(Object.class)) {
            return 0;
          } else if (READ_PACKET.isMatch(MethodInfo.fromMethod(method), null)) {
            readPacketIntercepted = true;
            return 1;
          } else {
            return 2;
          }
        }
      };
    }
   
    // Subclass the specific packet class
    ex.setSuperclass(old);
    ex.setCallbackFilter(filter);
    ex.setCallbackTypes(new Class<?>[] { NoOp.class, ReadPacketModifier.class, ReadPacketModifier.class });
    Class proxy = ex.createClass();
   
    // Create the proxy handlers
    ReadPacketModifier modifierReadPacket = new ReadPacketModifier(packetID, this, reporter, true);
    ReadPacketModifier modifierRest = new ReadPacketModifier(packetID, this, reporter, false);

    // Add a static reference
    Enhancer.registerStaticCallbacks(proxy, new Callback[] { NoOp.INSTANCE, modifierReadPacket, modifierRest });
   
    // Check that we found the read method
    if (!readPacketIntercepted) {
      reporter.reportWarning(this,
        Report.newBuilder(REPORT_CANNOT_FIND_READ_PACKET_METHOD).messageParam(packetID));
    }
   
    // Override values
    previous.put(packetID, old);
    registry.put(proxy, packetID);
    overwritten.put(packetID, proxy);
    lookup.setLookup(packetID, proxy);
    return true;
  }
 
  @Override
  @SuppressWarnings({"rawtypes", "deprecation"})
  public boolean removePacketHandler(PacketType type) {
    final int packetID = type.getLegacyId();
   
    if (!hasPacketHandler(type))
      return false;
   
    Map<Class, Integer> registry = PacketRegistry.getPacketToID();
    Map<Integer, Class> previous = PacketRegistry.getPreviousPackets();
    Map<Integer, Class> overwritten = PacketRegistry.getOverwrittenPackets();
   
    Class old = previous.get(packetID);
    Class proxy = PacketRegistry.getPacketClassFromType(type);
   
    lookup.setLookup(packetID, old);
    previous.remove(packetID);
    registry.remove(proxy);
    overwritten.remove(packetID);
    return true;
  }
 
  /**
   * Determine if the data a packet read must be buffered.
   * @param packetId - the packet to check.
   * @return TRUE if it does, FALSE otherwise.
   */
  @Deprecated
  public boolean requireInputBuffers(int packetId) {
    return manager.requireInputBuffer(packetId);
  }
 
  @SuppressWarnings("deprecation")
  @Override
  public boolean hasPacketHandler(PacketType type) {
    return PacketRegistry.getPreviousPackets().containsKey(type.getLegacyId());
  }
 
  @SuppressWarnings("deprecation")
  @Override
  public Set<PacketType> getPacketHandlers() {
    return PacketRegistry.toPacketTypes(PacketRegistry.getPreviousPackets().keySet(), Sender.CLIENT);
  }
 
  // Called from the ReadPacketModified monitor
  public PacketEvent packetRecieved(PacketContainer packet, InputStream input, byte[] buffered) {
    if (playerInjection.canRecievePackets()) {
      return playerInjection.handlePacketRecieved(packet, input, buffered);
    }
   
    try {
      Player client = playerInjection.getPlayerByConnection((DataInputStream) input);

      // Never invoke a event if we don't know where it's from
      if (client != null) {
        return packetRecieved(packet, client, buffered);
      } else {
        // The timeout elapsed!
        reporter.reportWarning(this, Report.newBuilder(REPORT_UNKNOWN_ORIGIN_FOR_PACKET).messageParam(input, packet.getType()));
        return null;
      }
     
    } catch (InterruptedException e) {
      // We will ignore this - it occurs when a player disconnects
      //reporter.reportDetailed(this, "Thread was interrupted.", e, packet, input);
      return null;
    }
  }
 
  @Override
  public PacketEvent packetRecieved(PacketContainer packet, Player client, byte[] buffered) {
    NetworkMarker marker = buffered != null ? new LegacyNetworkMarker(ConnectionSide.CLIENT_SIDE, buffered, packet.getType()) : null;
    PacketEvent event = PacketEvent.fromClient((Object) manager, packet, marker, client);
   
    manager.invokePacketRecieving(event);
    return event;
  }
 
  @Override
  @SuppressWarnings({"rawtypes", "deprecation"})
  public synchronized void cleanupAll() {
    Map<Integer, Class> overwritten = PacketRegistry.getOverwrittenPackets();
    Map<Integer, Class> previous = PacketRegistry.getPreviousPackets();
   
    // Remove every packet handler
    for (Integer id : previous.keySet().toArray(new Integer[0])) {
      removePacketHandler(PacketType.findLegacy(id, Sender.CLIENT));
      removePacketHandler(PacketType.findLegacy(id, Sender.SERVER));
    }
   
    overwritten.clear();
    previous.clear();
  }
}
TOP

Related Classes of com.comphenix.protocol.injector.packet.ProxyPacketInjector$PacketClassLookup

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.