230 lines
9.1 KiB
Java
230 lines
9.1 KiB
Java
/*
|
|
* 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;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.List;
|
|
import java.util.logging.Level;
|
|
|
|
import com.comphenix.protocol.PacketType;
|
|
import com.comphenix.protocol.ProtocolLibrary;
|
|
import com.comphenix.protocol.concurrency.AbstractConcurrentListenerMultimap;
|
|
import com.comphenix.protocol.error.ErrorReporter;
|
|
import com.comphenix.protocol.events.ListenerPriority;
|
|
import com.comphenix.protocol.events.PacketContainer;
|
|
import com.comphenix.protocol.events.PacketEvent;
|
|
import com.comphenix.protocol.events.PacketListener;
|
|
import com.comphenix.protocol.timing.TimedListenerManager;
|
|
import com.comphenix.protocol.timing.TimedListenerManager.ListenerType;
|
|
import com.comphenix.protocol.timing.TimedTracker;
|
|
|
|
import javax.annotation.Nullable;
|
|
|
|
/**
|
|
* Registry of synchronous packet listeners.
|
|
*
|
|
* @author Kristian
|
|
*/
|
|
public final class SortedPacketListenerList extends AbstractConcurrentListenerMultimap<PacketListener> {
|
|
// The current listener manager
|
|
private TimedListenerManager timedManager = TimedListenerManager.getInstance();
|
|
|
|
public SortedPacketListenerList() {
|
|
super();
|
|
}
|
|
|
|
/**
|
|
* Invokes the given packet event for every registered listener.
|
|
* @param reporter - the error reporter that will be used to inform about listener exceptions.
|
|
* @param event - the packet event to invoke.
|
|
*/
|
|
public void invokePacketRecieving(ErrorReporter reporter, PacketEvent event) {
|
|
Collection<PrioritizedListener<PacketListener>> list = getListener(event.getPacketType());
|
|
|
|
if (list == null)
|
|
return;
|
|
|
|
// The returned list is thread-safe
|
|
if (timedManager.isTiming()) {
|
|
for (PrioritizedListener<PacketListener> element : list) {
|
|
TimedTracker tracker = timedManager.getTracker(element.getListener(), ListenerType.SYNC_CLIENT_SIDE);
|
|
long token = tracker.beginTracking();
|
|
|
|
// Measure and record the execution time
|
|
invokeReceivingListener(reporter, event, element);
|
|
tracker.endTracking(token, event.getPacketType());
|
|
}
|
|
} else {
|
|
for (PrioritizedListener<PacketListener> element : list) {
|
|
invokeReceivingListener(reporter, event, element);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Invokes the given packet event for every registered listener of the given priority.
|
|
* @param reporter - the error reporter that will be used to inform about listener exceptions.
|
|
* @param event - the packet event to invoke.
|
|
* @param priorityFilter - the required priority for a listener to be invoked.
|
|
*/
|
|
public void invokePacketRecieving(ErrorReporter reporter, PacketEvent event, ListenerPriority priorityFilter) {
|
|
Collection<PrioritizedListener<PacketListener>> list = getListener(event.getPacketType());
|
|
|
|
if (list == null)
|
|
return;
|
|
|
|
// The returned list is thread-safe
|
|
if (timedManager.isTiming()) {
|
|
for (PrioritizedListener<PacketListener> element : list) {
|
|
if (element.getPriority() == priorityFilter) {
|
|
TimedTracker tracker = timedManager.getTracker(element.getListener(), ListenerType.SYNC_CLIENT_SIDE);
|
|
long token = tracker.beginTracking();
|
|
|
|
// Measure and record the execution time
|
|
invokeReceivingListener(reporter, event, element);
|
|
tracker.endTracking(token, event.getPacketType());
|
|
}
|
|
}
|
|
} else {
|
|
for (PrioritizedListener<PacketListener> element : list) {
|
|
if (element.getPriority() == priorityFilter) {
|
|
invokeReceivingListener(reporter, event, element);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Invoke a particular receiving listener.
|
|
* @param reporter - the error reporter.
|
|
* @param event - the related packet event.
|
|
* @param element - the listener to invoke.
|
|
*/
|
|
private void invokeReceivingListener(ErrorReporter reporter, PacketEvent event, PrioritizedListener<PacketListener> element) {
|
|
try {
|
|
event.setReadOnly(element.getPriority() == ListenerPriority.MONITOR);
|
|
element.getListener().onPacketReceiving(event);
|
|
|
|
} catch (OutOfMemoryError | ThreadDeath e) {
|
|
throw e;
|
|
} catch (Throwable e) {
|
|
// Minecraft doesn't want your Exception.
|
|
reporter.reportMinimal(element.getListener().getPlugin(), "onPacketReceiving(PacketEvent)", e,
|
|
event.getPacket().getHandle());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Invokes the given packet event for every registered listener.
|
|
* @param reporter - the error reporter that will be used to inform about listener exceptions.
|
|
* @param event - the packet event to invoke.
|
|
*/
|
|
public void invokePacketSending(ErrorReporter reporter, PacketEvent event) {
|
|
invokePacketSending(reporter, event, null);
|
|
}
|
|
|
|
/**
|
|
* Invokes the given packet event for every registered listener of the given priority.
|
|
* @param reporter - the error reporter that will be used to inform about listener exceptions.
|
|
* @param event - the packet event to invoke.
|
|
* @param priorityFilter - the priority for a listener to be invoked. If null is provided, every registered listener will be invoked
|
|
*/
|
|
public void invokePacketSending(ErrorReporter reporter, PacketEvent event, @Nullable ListenerPriority priorityFilter) {
|
|
invokeUnpackedPacketSending(reporter, event, priorityFilter);
|
|
if (event.getPacketType() == PacketType.Play.Server.BUNDLE && !event.isCancelled()) {
|
|
// unpack the bundle and invoke for each packet in the bundle
|
|
Iterable<PacketContainer> packets = event.getPacket().getPacketBundles().read(0);
|
|
List<PacketContainer> outPackets = new ArrayList<>();
|
|
for (PacketContainer subPacket : packets) {
|
|
if(subPacket == null) {
|
|
ProtocolLibrary.getPlugin().getLogger().log(Level.WARNING, "Failed to invoke packet event " + (priorityFilter == null ? "" : ("with priority " + priorityFilter)) + " in bundle because bundle contains null packet: " + packets, new Throwable());
|
|
continue;
|
|
}
|
|
PacketEvent subPacketEvent = PacketEvent.fromServer(this, subPacket, event.getNetworkMarker(), event.getPlayer());
|
|
invokeUnpackedPacketSending(reporter, subPacketEvent, priorityFilter);
|
|
|
|
if (!subPacketEvent.isCancelled()) {
|
|
// if the packet event has been cancelled, the packet will be removed from the bundle
|
|
PacketContainer packet = subPacketEvent.getPacket();
|
|
if(packet == null) {
|
|
ProtocolLibrary.getPlugin().getLogger().log(Level.WARNING, "null packet container returned for " + subPacketEvent, new Throwable());
|
|
} else if(packet.getHandle() == null) {
|
|
ProtocolLibrary.getPlugin().getLogger().log(Level.WARNING, "null packet handle returned for " + subPacketEvent, new Throwable());
|
|
} else {
|
|
outPackets.add(packet);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (packets.iterator().hasNext()) {
|
|
event.getPacket().getPacketBundles().write(0, outPackets);
|
|
} else {
|
|
// cancel entire packet if each individual packet has been cancelled
|
|
event.setCancelled(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void invokeUnpackedPacketSending(ErrorReporter reporter, PacketEvent event, @org.jetbrains.annotations.Nullable ListenerPriority priorityFilter) {
|
|
Collection<PrioritizedListener<PacketListener>> list = getListener(event.getPacketType());
|
|
|
|
if (list == null)
|
|
return;
|
|
|
|
if (timedManager.isTiming()) {
|
|
for (PrioritizedListener<PacketListener> element : list) {
|
|
if (priorityFilter == null || element.getPriority() == priorityFilter) {
|
|
TimedTracker tracker = timedManager.getTracker(element.getListener(), ListenerType.SYNC_SERVER_SIDE);
|
|
long token = tracker.beginTracking();
|
|
|
|
// Measure and record the execution time
|
|
invokeSendingListener(reporter, event, element);
|
|
tracker.endTracking(token, event.getPacketType());
|
|
}
|
|
}
|
|
} else {
|
|
for (PrioritizedListener<PacketListener> element : list) {
|
|
if (priorityFilter == null || element.getPriority() == priorityFilter) {
|
|
invokeSendingListener(reporter, event, element);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Invoke a particular sending listener.
|
|
* @param reporter - the error reporter.
|
|
* @param event - the related packet event.
|
|
* @param element - the listener to invoke.
|
|
*/
|
|
private void invokeSendingListener(ErrorReporter reporter, PacketEvent event, PrioritizedListener<PacketListener> element) {
|
|
try {
|
|
event.setReadOnly(element.getPriority() == ListenerPriority.MONITOR);
|
|
element.getListener().onPacketSending(event);
|
|
|
|
} catch (OutOfMemoryError | ThreadDeath e) {
|
|
throw e;
|
|
} catch (Throwable e) {
|
|
// Minecraft doesn't want your Exception.
|
|
reporter.reportMinimal(element.getListener().getPlugin(), "onPacketSending(PacketEvent)", e,
|
|
event.getPacket().getHandle());
|
|
}
|
|
}
|
|
}
|