package com.bergerkiller.bukkit.common.internal.network; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import com.bergerkiller.bukkit.common.conversion.Conversion; import com.bergerkiller.bukkit.common.events.PacketReceiveEvent; import com.bergerkiller.bukkit.common.events.PacketSendEvent; import com.bergerkiller.bukkit.common.internal.PacketHandler; import com.bergerkiller.bukkit.common.protocol.CommonPacket; import com.bergerkiller.bukkit.common.protocol.PacketListener; import com.bergerkiller.bukkit.common.protocol.PacketMonitor; import com.bergerkiller.bukkit.common.protocol.PacketType; import com.bergerkiller.bukkit.common.utils.CommonUtil; import com.bergerkiller.bukkit.common.utils.PlayerUtil; import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.events.ListenerOptions; import com.comphenix.protocol.events.ListenerPriority; import com.comphenix.protocol.events.ListeningWhitelist; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.injector.GamePhase; import com.comphenix.protocol.injector.PlayerLoggedOutException; import com.comphenix.protocol.injector.packet.PacketRegistry; /** * A packet handler implementation that uses ProtocolLib packet listeners */ public class ProtocolLibPacketHandler implements PacketHandler { public static final String LIB_ROOT = "com.comphenix.protocol."; private final List<CommonPacketMonitor> monitors = new ArrayList<CommonPacketMonitor>(); private final List<CommonPacketListener> listeners = new ArrayList<CommonPacketListener>(); @Override public void onPlayerJoin(Player player) { } @Override public boolean onEnable() { // Check whether all required classes are available Class<?> manager = CommonUtil.getClass(LIB_ROOT + "ProtocolManager"); Class<?> packetContainer = CommonUtil.getClass(LIB_ROOT + "events.PacketContainer"); if (manager == null || packetContainer == null) { return false; } return true; } @Override public boolean onDisable() { return true; } @Override public String getName() { return "the ProtocolLib library"; } @Override public Collection<Plugin> getListening(PacketType packetType) { Set<Plugin> plugins = new HashSet<Plugin>(); // Obtain all plugins that have a listener (ignore monitors) boolean outGoing = packetType.isOutGoing(); com.comphenix.protocol.PacketType comType = getPacketType(packetType); for (com.comphenix.protocol.events.PacketListener listener : ProtocolLibrary.getProtocolManager().getPacketListeners()) { final ListeningWhitelist whitelist; if (outGoing) { whitelist = listener.getSendingWhitelist(); } else { whitelist = listener.getReceivingWhitelist(); } if (whitelist.getPriority() != ListenerPriority.MONITOR && whitelist.getTypes().contains(comType)) { plugins.add(listener.getPlugin()); } } return plugins; } @Override public void receivePacket(Player player, Object packet) { if (isNPCPlayer(player) || PlayerUtil.isDisconnected(player)) { return; } PacketContainer toReceive = new PacketContainer(getPacketType(packet.getClass()), packet); try{ ProtocolLibrary.getProtocolManager().recieveClientPacket(player, toReceive); } catch (PlayerLoggedOutException ex) { // Ignore } catch (Exception e) { throw new RuntimeException("Error while receiving packet:", e); } } @Override public void sendPacket(Player player, Object packet, boolean throughListeners) { if (isNPCPlayer(player) || PlayerUtil.isDisconnected(player)) { return; } PacketContainer toSend = new PacketContainer(getPacketType(packet.getClass()), packet); try { if (throughListeners) { // Send it through the listeners ProtocolLibrary.getProtocolManager().sendServerPacket(player, toSend); } else { // Silent - do not send it through listeners, only through monitors sendSilentPacket(player, toSend); } } catch (PlayerLoggedOutException ex) { // Ignore } catch (Exception e) { throw new RuntimeException("Error while sending packet:", e); } } private boolean isNPCPlayer(Player player) { // Is this check still needed? Object handle = Conversion.toEntityHandle.convert(player); return !handle.getClass().equals(CommonUtil.getNMSClass("EntityPlayer")); } private void sendSilentPacket(Player player, PacketContainer packet) throws InvocationTargetException { ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet, null, false); } @Override public void removePacketListeners(Plugin plugin) { ProtocolLibrary.getProtocolManager().removePacketListeners(plugin); } @Override public void removePacketListener(PacketListener listener) { Iterator<CommonPacketListener> iter = listeners.iterator(); while (iter.hasNext()) { CommonPacketListener cpl = iter.next(); if (cpl.listener == listener) { ProtocolLibrary.getProtocolManager().removePacketListener(cpl); iter.remove(); } } } @Override public void removePacketMonitor(PacketMonitor monitor) { Iterator<CommonPacketMonitor> iter = monitors.iterator(); while (iter.hasNext()) { CommonPacketMonitor cpm = iter.next(); if (cpm.monitor == monitor) { ProtocolLibrary.getProtocolManager().removePacketListener(cpm); iter.remove(); } } } @Override public void addPacketListener(Plugin plugin, PacketListener listener, PacketType[] types) { CommonPacketListener commonListener = new CommonPacketListener(plugin, listener, types); ProtocolLibrary.getProtocolManager().addPacketListener(commonListener); this.listeners.add(commonListener); } @Override public void addPacketMonitor(Plugin plugin, PacketMonitor monitor, PacketType[] types) { CommonPacketMonitor commonMonitor = new CommonPacketMonitor(plugin, monitor, types); ProtocolLibrary.getProtocolManager().addPacketListener(commonMonitor); this.monitors.add(commonMonitor); } @Override public void transfer(PacketHandler to) { for (CommonPacketListener listener : listeners) { to.addPacketListener(listener.getPlugin(), listener.listener, listener.types); } for (CommonPacketMonitor monitor : monitors) { to.addPacketMonitor(monitor.getPlugin(), monitor.monitor, monitor.types); } } @Override public long getPendingBytes(Player player) { return PacketHandlerHooked.calculatePendingBytes(player); } private static com.comphenix.protocol.PacketType getPacketType(PacketType commonType) { return getPacketType(commonType.getType()); } private static com.comphenix.protocol.PacketType getPacketType(Class<?> packetClass) { return PacketRegistry.getPacketType(packetClass); } private static class CommonPacketMonitor extends CommonPacketAdapter { public final PacketMonitor monitor; public CommonPacketMonitor(Plugin plugin, PacketMonitor monitor, PacketType[] types) { super(plugin, ListenerPriority.MONITOR, types); this.monitor = monitor; } @Override public void onPacketReceiving(PacketEvent event) { monitor.onMonitorPacketReceive(new CommonPacket(event.getPacket().getHandle()), event.getPlayer()); } @Override public void onPacketSending(PacketEvent event) { monitor.onMonitorPacketSend(new CommonPacket(event.getPacket().getHandle()), event.getPlayer()); } } private static class CommonPacketListener extends CommonPacketAdapter { public final PacketListener listener; public CommonPacketListener(Plugin plugin, PacketListener listener, PacketType[] types) { super(plugin, ListenerPriority.NORMAL, types); this.listener = listener; } @Override public void onPacketReceiving(PacketEvent event) { CommonPacket packet = new CommonPacket(event.getPacket().getHandle()); PacketReceiveEvent receiveEvent = new PacketReceiveEvent(event.getPlayer(), packet); receiveEvent.setCancelled(event.isCancelled()); listener.onPacketReceive(receiveEvent); event.setCancelled(receiveEvent.isCancelled()); } @Override public void onPacketSending(PacketEvent event) { CommonPacket packet = new CommonPacket(event.getPacket().getHandle()); PacketSendEvent sendEvent = new PacketSendEvent(event.getPlayer(), packet); sendEvent.setCancelled(event.isCancelled()); listener.onPacketSend(sendEvent); event.setCancelled(sendEvent.isCancelled()); } } private static abstract class CommonPacketAdapter implements com.comphenix.protocol.events.PacketListener { private final Plugin plugin; public final PacketType[] types; private final ListeningWhitelist receiving; private final ListeningWhitelist sending; public CommonPacketAdapter(Plugin plugin, ListenerPriority priority, PacketType[] types) { this.plugin = plugin; this.types = types; this.receiving = getWhiteList(priority, types, true); this.sending = getWhiteList(priority, types, false); } private static ListeningWhitelist getWhiteList(ListenerPriority priority, PacketType[] types, boolean receiving) { List<com.comphenix.protocol.PacketType> comTypes = new ArrayList<com.comphenix.protocol.PacketType>(); for (PacketType type : types) { if ((!type.isOutGoing()) != receiving) { continue; } com.comphenix.protocol.PacketType comType = getPacketType(type); comTypes.add(comType); } return ListeningWhitelist.newBuilder().priority(priority).types(comTypes) .gamePhase(GamePhase.PLAYING).options(new ListenerOptions[0]).build(); } @Override public Plugin getPlugin() { return plugin; } @Override public ListeningWhitelist getReceivingWhitelist() { return receiving; } @Override public ListeningWhitelist getSendingWhitelist() { return sending; } } }