Skip to content

Commit

Permalink
feat: improve packet listener tracking
Browse files Browse the repository at this point in the history
fix: terminal packets breaking protocol getter on inbound packets
  • Loading branch information
Ingrim4 committed May 25, 2024
1 parent ec8c269 commit eaa38b7
Show file tree
Hide file tree
Showing 17 changed files with 543 additions and 582 deletions.
54 changes: 31 additions & 23 deletions src/main/java/com/comphenix/protocol/injector/ListenerInvoker.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,35 @@
*/
public interface ListenerInvoker {

/**
* Invokes the given packet event for every registered listener.
*
* @param event - the packet event to invoke.
*/
void invokePacketReceiving(PacketEvent event);

/**
* Invokes the given packet event for every registered listener.
*
* @param event - the packet event to invoke.
*/
void invokePacketSending(PacketEvent event);

/**
* Retrieve the associated type of a packet.
*
* @param packet - the packet.
* @return The packet type.
* @deprecated use {@link com.comphenix.protocol.injector.packet.PacketRegistry#getPacketType(PacketType.Protocol, Class)} instead.
*/
@Deprecated
PacketType getPacketType(Object packet);
boolean hasInboundListener(PacketType packetType);

boolean hasOutboundListener(PacketType packetType);

boolean hasMainThreadListener(PacketType packetType);

/**
* Invokes the given packet event for every registered listener.
*
* @param event - the packet event to invoke.
*/
void invokePacketReceiving(PacketEvent event);

/**
* Invokes the given packet event for every registered listener.
*
* @param event - the packet event to invoke.
*/
void invokePacketSending(PacketEvent event);

/**
* Retrieve the associated type of a packet.
*
* @param packet - the packet.
* @return The packet type.
* @deprecated use
* {@link com.comphenix.protocol.injector.packet.PacketRegistry#getPacketType(PacketType.Protocol, Class)}
* instead.
*/
@Deprecated
PacketType getPacketType(Object packet);
}
171 changes: 68 additions & 103 deletions src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.comphenix.protocol.injector.collection;

import javax.annotation.Nullable;

import com.comphenix.protocol.concurrent.PacketTypeListenerSet;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.ListeningWhitelist;
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;

public class InboundPacketListenerSet extends PacketListenerSet {

public InboundPacketListenerSet(PacketTypeListenerSet mainThreadPacketTypes, ErrorReporter errorReporter) {
super(mainThreadPacketTypes, errorReporter);
}

@Override
protected ListeningWhitelist getListeningWhitelist(PacketListener packetListener) {
return packetListener.getReceivingWhitelist();
}

/**
* Invokes the given packet event for every registered listener of the given
* priority.
*
* @param event - the packet event to invoke.
* @param priorityFilter - the required priority for a listener to be invoked.
*/
@Override
public void invoke(PacketEvent event, @Nullable ListenerPriority priorityFilter) {
Iterable<PacketListener> listeners = this.map.get(event.getPacketType());

TimedListenerManager timedManager = TimedListenerManager.getInstance();
if (timedManager.isTiming()) {
for (PacketListener element : listeners) {
if (priorityFilter == null || element.getReceivingWhitelist().getPriority() == priorityFilter) {
TimedTracker tracker = timedManager.getTracker(element, ListenerType.SYNC_CLIENT_SIDE);
long token = tracker.beginTracking();

// Measure and record the execution time
invokeReceivingListener(event, element);
tracker.endTracking(token, event.getPacketType());
}
}
} else {
for (PacketListener element : listeners) {
if (priorityFilter == null || element.getReceivingWhitelist().getPriority() == priorityFilter) {
invokeReceivingListener(event, element);
}
}
}
}

/**
* Invoke a particular receiving listener.
*
* @param reporter - the error reporter.
* @param event - the related packet event.
* @param listener - the listener to invoke.
*/
private void invokeReceivingListener(PacketEvent event, PacketListener listener) {
try {
event.setReadOnly(listener.getReceivingWhitelist().getPriority() == ListenerPriority.MONITOR);
listener.onPacketReceiving(event);
} catch (OutOfMemoryError | ThreadDeath e) {
throw e;
} catch (Throwable e) {
// Minecraft doesn't want your Exception.
errorReporter.reportMinimal(listener.getPlugin(), "onPacketReceiving(PacketEvent)", e,
event.getPacket().getHandle());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package com.comphenix.protocol.injector.collection;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;

import javax.annotation.Nullable;

import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.concurrent.PacketTypeListenerSet;
import com.comphenix.protocol.error.ErrorReporter;
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.events.PacketListener;
import com.comphenix.protocol.timing.TimedListenerManager;
import com.comphenix.protocol.timing.TimedListenerManager.ListenerType;
import com.comphenix.protocol.timing.TimedTracker;

public class OutboundPacketListenerSet extends PacketListenerSet {

public OutboundPacketListenerSet(PacketTypeListenerSet mainThreadPacketTypes, ErrorReporter errorReporter) {
super(mainThreadPacketTypes, errorReporter);
}

@Override
protected ListeningWhitelist getListeningWhitelist(PacketListener packetListener) {
return packetListener.getSendingWhitelist();
}

/**
* Invokes the given packet event for every registered listener of the given
* priority.
*
* @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
*/
@Override
public void invoke(PacketEvent event, @Nullable ListenerPriority priorityFilter) {
invokeUnpackedPacketSending(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(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(PacketEvent event, @Nullable ListenerPriority priorityFilter) {
Iterable<PacketListener> listeners = this.map.get(event.getPacketType());

TimedListenerManager timedManager = TimedListenerManager.getInstance();
if (timedManager.isTiming()) {
for (PacketListener element : listeners) {
if (priorityFilter == null || element.getSendingWhitelist().getPriority() == priorityFilter) {
TimedTracker tracker = timedManager.getTracker(element, ListenerType.SYNC_SERVER_SIDE);
long token = tracker.beginTracking();

// Measure and record the execution time
invokeSendingListener(event, element);
tracker.endTracking(token, event.getPacketType());
}
}
} else {
for (PacketListener element : listeners) {
if (priorityFilter == null || element.getSendingWhitelist().getPriority() == priorityFilter) {
invokeSendingListener(event, element);
}
}
}
}

/**
* Invoke a particular sending listener.
*
* @param reporter - the error reporter.
* @param event - the related packet event.
* @param listener - the listener to invoke.
*/
private void invokeSendingListener(PacketEvent event, PacketListener listener) {
try {
event.setReadOnly(listener.getSendingWhitelist().getPriority() == ListenerPriority.MONITOR);
listener.onPacketSending(event);
} catch (OutOfMemoryError | ThreadDeath e) {
throw e;
} catch (Throwable e) {
// Minecraft doesn't want your Exception.
errorReporter.reportMinimal(listener.getPlugin(), "onPacketSending(PacketEvent)", e,
event.getPacket().getHandle());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.comphenix.protocol.injector.collection;

import java.util.Set;

import javax.annotation.Nullable;

import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.concurrent.PacketTypeListenerSet;
import com.comphenix.protocol.concurrent.PacketTypeMultiMap;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.ListenerOptions;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.google.common.collect.ImmutableSet;

public abstract class PacketListenerSet {

private static final ReportType UNSUPPORTED_PACKET = new ReportType(
"Plugin %s tried to register listener for unknown packet %s [direction: from %s]");

protected final PacketTypeMultiMap<PacketListener> map = new PacketTypeMultiMap<>();

protected final PacketTypeListenerSet mainThreadPacketTypes;
protected final ErrorReporter errorReporter;

public PacketListenerSet(PacketTypeListenerSet mainThreadPacketTypes, ErrorReporter errorReporter) {
this.mainThreadPacketTypes = mainThreadPacketTypes;
this.errorReporter = errorReporter;
}

protected abstract ListeningWhitelist getListeningWhitelist(PacketListener packetListener);

public void addListener(PacketListener packetListener) {
ListeningWhitelist listeningWhitelist = getListeningWhitelist(packetListener);
this.map.put(listeningWhitelist, packetListener);

Set<ListenerOptions> options = listeningWhitelist.getOptions();
for (PacketType packetType : listeningWhitelist.getTypes()) {
if (!packetType.isAsyncForced() && !options.contains(ListenerOptions.ASYNC)) {
this.mainThreadPacketTypes.add(packetType, packetListener);
}

Set<PacketType> supportedPacketTypes = (packetType.getSender() == Sender.SERVER)
? PacketRegistry.getServerPacketTypes()
: PacketRegistry.getClientPacketTypes();

if (!supportedPacketTypes.contains(packetType)) {
this.errorReporter.reportWarning(this, Report.newBuilder(UNSUPPORTED_PACKET)
.messageParam(PacketAdapter.getPluginName(packetListener), packetType, packetType.getSender())
.build());
}
}
}

public void removeListener(PacketListener packetListener) {
ListeningWhitelist listeningWhitelist = getListeningWhitelist(packetListener);
this.map.remove(listeningWhitelist, packetListener);

for (PacketType packetType : listeningWhitelist.getTypes()) {
this.mainThreadPacketTypes.remove(packetType, packetListener);
}
}

public final boolean containsPacketType(PacketType packetType) {
return this.map.contains(packetType);
}

public final ImmutableSet<PacketType> getPacketTypes() {
return this.map.getPacketTypes();
}

public void invoke(PacketEvent event) {
this.invoke(event, null);
}

public abstract void invoke(PacketEvent event, @Nullable ListenerPriority priorityFilter);

public void clear() {
this.map.clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;

/**
Expand All @@ -22,7 +23,7 @@ public interface ChannelListener {
* @param marker - the network marker.
* @return The packet even that was passed to the listeners, with a possible packet change, or NULL.
*/
PacketEvent onPacketSending(Injector injector, Object packet, NetworkMarker marker);
PacketEvent onPacketSending(Injector injector, PacketContainer packet, NetworkMarker marker);

/**
* Invoked when a packet is being received from a client.
Expand All @@ -34,23 +35,11 @@ public interface ChannelListener {
* @param marker - the associated network marker, if any.
* @return The packet even that was passed to the listeners, with a possible packet change, or NULL.
*/
PacketEvent onPacketReceiving(Injector injector, Object packet, NetworkMarker marker);
PacketEvent onPacketReceiving(Injector injector, PacketContainer packet, NetworkMarker marker);

/**
* Determine if there is a packet listener for the given packet.
*
* @param packetClass - the packet class to check.
* @return TRUE if there is such a listener, FALSE otherwise.
*/
boolean hasListener(Class<?> packetClass);
boolean hasInboundListener(PacketType packetType);

/**
* Determine if there is a server packet listener that must be executed on the main thread.
*
* @param packetClass - the packet class to check.
* @return TRUE if there is, FALSE otherwise.
*/
boolean hasMainThreadListener(Class<?> packetClass);
boolean hasOutboundListener(PacketType packetType);

boolean hasMainThreadListener(PacketType type);

Expand Down
Loading

0 comments on commit eaa38b7

Please sign in to comment.