Archiviert
13
0

Run onPacketSending() on the main thread if not otherwise stated.

Now onPacketSending() should only be executed on the main thread, 
unless the listener has specified ListenerOption.ASYNC. This option
is off by default, however.

This ensures that older plugins that assume onPacketSending() doesn't
crash the server. They SHOULD use the ASYNC option if possible, as
this explicit synchronization will slow down the STATUS 
protocol somewhat.
Dieser Commit ist enthalten in:
Kristian S. Stangeland 2013-12-07 19:14:03 +01:00
Ursprung 4d11cfa8e8
Commit 029b19d94c
14 geänderte Dateien mit 197 neuen und 64 gelöschten Zeilen

Datei anzeigen

@ -17,5 +17,10 @@ public enum ListenerOptions {
* Disable the automatic game phase detection that will normally force {@link GamePhase#LOGIN} when
* a packet ID is known to be transmitted during login.
*/
DISABLE_GAMEPHASE_DETECTION;
DISABLE_GAMEPHASE_DETECTION,
/**
* Notify ProtocolLib that {@link PacketListener#onPacketSending(PacketEvent)} is thread safe.
*/
ASYNC;
}

Datei anzeigen

@ -85,6 +85,17 @@ public abstract class PacketAdapter implements PacketListener {
this(params(plugin, Iterables.toArray(types, PacketType.class)).listenerPriority(listenerPriority));
}
/**
* Initialize a packet listener with the given parameters.
* @param plugin - the plugin.
* @param listenerPriority - the priority.
* @param types - the packet types.
* @param options - the options.
*/
public PacketAdapter(Plugin plugin, ListenerPriority listenerPriority, Iterable<? extends PacketType> types, ListenerOptions... options) {
this(params(plugin, Iterables.toArray(types, PacketType.class)).listenerPriority(listenerPriority).options(options));
}
/**
* Initialize a packet listener with the given parameters.
* @param plugin - the plugin.
@ -391,7 +402,6 @@ public abstract class PacketAdapter implements PacketListener {
* @return Helper object.
*/
public static AdapterParameteters params(Plugin plugin, PacketType... packets) {
return new AdapterParameteters().plugin(plugin).types(packets);
}
@ -491,12 +501,58 @@ public abstract class PacketAdapter implements PacketListener {
return this;
}
/**
* Set listener options that decide whether or not to intercept the raw packet data as read from the network stream.
* <p>
* The default is to disable this raw packet interception.
* @param options - every option to use.
* @return This builder, for chaining.
*/
public AdapterParameteters options(@Nonnull Set<? extends ListenerOptions> options) {
Preconditions.checkNotNull(options, "options cannot be NULL.");
this.options = options.toArray(new ListenerOptions[0]);
return this;
}
/**
* Add a given option to the current builder.
* @param option - the option to add.
* @return This builder, for chaining.
*/
private AdapterParameteters addOption(ListenerOptions option) {
if (options == null) {
return options(option);
} else {
Set<ListenerOptions> current = Sets.newHashSet(options);
current.add(option);
return options(current);
}
}
/**
* Set the listener option to {@link ListenerOptions#INTERCEPT_INPUT_BUFFER}, causing ProtocolLib to read the raw packet data from the network stream.
* @return This builder, for chaining.
*/
public AdapterParameteters optionIntercept() {
return options(ListenerOptions.INTERCEPT_INPUT_BUFFER);
return addOption(ListenerOptions.INTERCEPT_INPUT_BUFFER);
}
/**
* Set the listener option to {@link ListenerOptions#DISABLE_GAMEPHASE_DETECTION}, causing ProtocolLib to ignore automatic game phase detection.
* <p>
* This is no longer relevant in 1.7.2.
* @return This builder, for chaining.
*/
public AdapterParameteters optionManualGamePhase() {
return addOption(ListenerOptions.DISABLE_GAMEPHASE_DETECTION);
}
/**
* Set the listener option to {@link ListenerOptions#ASYNC}, causing ProtocolLib to ignore automatic game phase detection.
* @return This builder, for chaining.
*/
public AdapterParameteters optionAsync() {
return addOption(ListenerOptions.DISABLE_GAMEPHASE_DETECTION);
}
/**

Datei anzeigen

@ -34,8 +34,8 @@ public interface PacketListener {
* <p>
* This method is executed on the main thread in 1.6.4 and earlier, and thus the Bukkit API is safe to use.
* <p>
* <b>Warning:</b> In 1.7.2 and later, login and status packets are executed on a worker thread.
* Call {@link PacketEvent#isAsync()} to detect this in your listener.
* In Minecraft 1.7.2 and later, this method MAY be executed asynchronously, but only if {@link ListenerOptions#ASYNC}
* have been specified in the listener. This is off by default.
* @param event - the packet that should be sent.
*/
public void onPacketSending(PacketEvent event);

Datei anzeigen

@ -613,7 +613,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
if (side.isForServer()) {
// Note that we may update the packet list here
if (!knowsServerPackets || PacketRegistry.getServerPacketTypes().contains(type))
playerInjection.addPacketHandler(type);
playerInjection.addPacketHandler(type, listener.getSendingWhitelist().getOptions());
else
reporter.reportWarning(this,
Report.newBuilder(REPORT_UNSUPPORTED_SERVER_PACKET_ID).messageParam(PacketAdapter.getPluginName(listener), type)
@ -623,7 +623,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
// As above, only for client packets
if (side.isForClient() && packetInjector != null) {
if (!knowsClientPackets || PacketRegistry.getClientPacketTypes().contains(type))
packetInjector.addPacketHandler(type);
packetInjector.addPacketHandler(type, listener.getReceivingWhitelist().getOptions());
else
reporter.reportWarning(this,
Report.newBuilder(REPORT_UNSUPPORTED_CLIENT_PACKET_ID).messageParam(PacketAdapter.getPluginName(listener), type)

Datei anzeigen

@ -25,7 +25,7 @@ import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.ConnectionSide;
@ -52,47 +52,6 @@ class ChannelInjector extends ByteToMessageDecoder {
public static final ReportType REPORT_CANNOT_INTERCEPT_SERVER_PACKET = new ReportType("Unable to intercept a written server packet.");
public static final ReportType REPORT_CANNOT_INTERCEPT_CLIENT_PACKET = new ReportType("Unable to intercept a read client packet.");
/**
* Represents a listener for received or sent packets.
* @author Kristian
*/
interface ChannelListener {
/**
* Invoked when a packet is being sent to the client.
* <p>
* This is invoked on the main thread.
* @param injector - the channel injector.
* @param packet - the packet.
* @param marker - the associated network marker, if any.
* @return The new packet, if it should be changed, or NULL to cancel.
*/
public Object onPacketSending(ChannelInjector injector, Object packet, NetworkMarker marker);
/**
* Invoked when a packet is being received from a client.
* <p>
* This is invoked on an asynchronous worker thread.
* @param injector - the channel injector.
* @param packet - the packet.
* @param marker - the associated network marker, if any.
* @return The new packet, if it should be changed, or NULL to cancel.
*/
public Object onPacketReceiving(ChannelInjector injector, Object packet, NetworkMarker marker);
/**
* Determine if we need the buffer data of a given client side packet.
* @param packetClass - the packet class.
* @return TRUE if we do, FALSE otherwise.
*/
public boolean includeBuffer(Class<?> packetClass);
/**
* Retrieve the current error reporter.
* @return The error reporter.
*/
public ErrorReporter getReporter();
}
private static final ConcurrentMap<Player, ChannelInjector> cachedInjector = new MapMaker().weakKeys().makeMap();
// Saved accessors
@ -340,10 +299,20 @@ class ChannelInjector extends ByteToMessageDecoder {
// Try again, in case this packet was sent directly in the event loop
if (event == null && !processedPackets.remove(packet)) {
Class<?> clazz = packet.getClass();
// Schedule the transmission on the main thread instead
if (channelListener.hasMainThreadListener(clazz)) {
// Delay the packet
scheduleMainThread(marker, packet);
packet = null;
} else {
packet = processSending(packet);
marker = getMarker(packet);
event = markerEvent.remove(marker);
}
}
// Process output handler
if (packet != null && event != null && NetworkMarker.hasOutputHandlers(marker)) {
@ -369,6 +338,15 @@ class ChannelInjector extends ByteToMessageDecoder {
}
}
private void scheduleMainThread(final NetworkMarker marker, final Object packetCopy) {
ProtocolLibrary.getExecutorSync().execute(new Runnable() {
@Override
public void run() {
sendServerPacket(packetCopy, marker, true);
}
});
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuffer, List<Object> packets) throws Exception {
try {
@ -620,7 +598,7 @@ class ChannelInjector extends ByteToMessageDecoder {
* Represents a socket injector that foreards to the current channel injector.
* @author Kristian
*/
private static class ChannelSocketInjector implements SocketInjector {
static class ChannelSocketInjector implements SocketInjector {
private final ChannelInjector injector;
public ChannelSocketInjector(ChannelInjector injector) {

Datei anzeigen

@ -0,0 +1,59 @@
package com.comphenix.protocol.injector.netty;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.events.NetworkMarker;
/**
* Represents a listener for received or sent packets.
* @author Kristian
*/
interface ChannelListener {
/**
* Invoked when a packet is being sent to the client.
* <p>
* This is invoked on the main thread.
* @param injector - the channel injector.
* @param packet - the packet.
* @param marker - the associated network marker, if any.
* @return The new packet, if it should be changed, or NULL to cancel.
*/
public Object onPacketSending(ChannelInjector injector, Object packet, NetworkMarker marker);
/**
* Invoked when a packet is being received from a client.
* <p>
* This is invoked on an asynchronous worker thread.
* @param injector - the channel injector.
* @param packet - the packet.
* @param marker - the associated network marker, if any.
* @return The new packet, if it should be changed, or NULL to cancel.
*/
public Object onPacketReceiving(ChannelInjector injector, Object 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.
*/
public boolean hasListener(Class<?> packetClass);
/**
* 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.
*/
public boolean hasMainThreadListener(Class<?> packetClass);
/**
* Determine if we need the buffer data of a given client side packet.
* @param packetClass - the packet class.
* @return TRUE if we do, FALSE otherwise.
*/
public boolean includeBuffer(Class<?> packetClass);
/**
* Retrieve the current error reporter.
* @return The error reporter.
*/
public ErrorReporter getReporter();
}

Datei anzeigen

@ -22,11 +22,11 @@ import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.concurrency.PacketTypeSet;
import com.comphenix.protocol.error.ErrorReporter;
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.netty.ChannelInjector.ChannelListener;
import com.comphenix.protocol.injector.packet.PacketInjector;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
@ -47,8 +47,10 @@ public class NettyProtocolInjector implements ChannelListener {
private List<VolatileField> bootstrapFields = Lists.newArrayList();
// Different sending filters
private PacketTypeSet queuedFilters = new PacketTypeSet();
private PacketTypeSet sendingFilters = new PacketTypeSet();
private PacketTypeSet reveivedFilters = new PacketTypeSet();
// Packets that must be executed on the main thread
private PacketTypeSet mainThreadFilters = new PacketTypeSet();
// Which packets are buffered
private PacketTypeSet bufferedPackets = new PacketTypeSet();
@ -118,6 +120,16 @@ public class NettyProtocolInjector implements ChannelListener {
}
}
@Override
public boolean hasListener(Class<?> packetClass) {
return reveivedFilters.contains(packetClass) || sendingFilters.contains(packetClass);
}
@Override
public boolean hasMainThreadListener(Class<?> packetClass) {
return mainThreadFilters.contains(packetClass);
}
@Override
public ErrorReporter getReporter() {
return reporter;
@ -175,7 +187,7 @@ public class NettyProtocolInjector implements ChannelListener {
public Object onPacketSending(ChannelInjector injector, Object packet, NetworkMarker marker) {
Class<?> clazz = packet.getClass();
if (queuedFilters.contains(clazz)) {
if (sendingFilters.contains(clazz)) {
// Check for ignored packets
if (injector.unignorePacket(packet)) {
return packet;
@ -248,8 +260,9 @@ public class NettyProtocolInjector implements ChannelListener {
return event;
}
// Server side
public PlayerInjectionHandler getPlayerInjector() {
return new AbstractPlayerHandler(queuedFilters) {
return new AbstractPlayerHandler(sendingFilters) {
private ChannelListener listener = NettyProtocolInjector.this;
@Override
@ -263,6 +276,19 @@ public class NettyProtocolInjector implements ChannelListener {
return true;
}
@Override
public void addPacketHandler(PacketType type, Set<ListenerOptions> options) {
if (options != null && !options.contains(ListenerOptions.ASYNC))
mainThreadFilters.addType(type);
super.addPacketHandler(type, options);
}
@Override
public void removePacketHandler(PacketType type) {
mainThreadFilters.removeType(type);
super.removePacketHandler(type);
}
@Override
public boolean uninjectPlayer(Player player) {
ChannelInjector.fromPlayer(player, listener).close();
@ -303,6 +329,7 @@ public class NettyProtocolInjector implements ChannelListener {
* Retrieve a view of this protocol injector as a packet injector.
* @return The packet injector.
*/
// Client side
public PacketInjector getPacketInjector() {
return new AbstractPacketInjector(reveivedFilters) {
@Override

Datei anzeigen

@ -5,6 +5,7 @@ import java.util.Set;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.ListenerOptions;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
@ -31,9 +32,10 @@ public interface PacketInjector {
/**
* Start intercepting packets with the given packet type.
* @param type - the type of the packets to start intercepting.
* @param options - any listener options.
* @return TRUE if we didn't already intercept these packets, FALSE otherwise.
*/
public abstract boolean addPacketHandler(PacketType type);
public abstract boolean addPacketHandler(PacketType type, Set<ListenerOptions> options);
/**
* Stop intercepting packets with the given packet type.

Datei anzeigen

@ -38,6 +38,7 @@ 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;
@ -204,7 +205,7 @@ class ProxyPacketInjector implements PacketInjector {
@Override
@SuppressWarnings({"rawtypes", "deprecation"})
public boolean addPacketHandler(PacketType type) {
public boolean addPacketHandler(PacketType type, Set<ListenerOptions> options) {
final int packetID = type.getLegacyId();
if (hasPacketHandler(type))

Datei anzeigen

@ -8,6 +8,7 @@ import java.util.Set;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.ListenerOptions;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
@ -62,8 +63,9 @@ public interface PlayerInjectionHandler {
/**
* Add an underlying packet handler of the given type.
* @param type - packet type to register.
* @param options - any specified listener options.
*/
public abstract void addPacketHandler(PacketType type);
public abstract void addPacketHandler(PacketType type, Set<ListenerOptions> options);
/**
* Remove an underlying packet handler of this type.

Datei anzeigen

@ -42,6 +42,7 @@ import com.comphenix.protocol.concurrency.IntegerSet;
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.NetworkMarker;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
@ -203,7 +204,7 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
}
@Override
public void addPacketHandler(PacketType type) {
public void addPacketHandler(PacketType type, Set<ListenerOptions> options) {
sendingFilters.add(type.getLegacyId());
}

Datei anzeigen

@ -4,6 +4,7 @@ import java.util.Set;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.concurrency.PacketTypeSet;
import com.comphenix.protocol.events.ListenerOptions;
import com.comphenix.protocol.injector.packet.PacketInjector;
public abstract class AbstractPacketInjector implements PacketInjector {
@ -25,7 +26,7 @@ public abstract class AbstractPacketInjector implements PacketInjector {
}
@Override
public boolean addPacketHandler(PacketType type) {
public boolean addPacketHandler(PacketType type, Set<ListenerOptions> options) {
reveivedFilters.addType(type);
return true;
}

Datei anzeigen

@ -7,6 +7,7 @@ import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.concurrency.PacketTypeSet;
import com.comphenix.protocol.events.ListenerOptions;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.GamePhase;
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
@ -30,7 +31,7 @@ public abstract class AbstractPlayerHandler implements PlayerInjectionHandler {
}
@Override
public void addPacketHandler(PacketType type) {
public void addPacketHandler(PacketType type, Set<ListenerOptions> options) {
sendingFilters.addType(type);
}

Datei anzeigen

@ -35,7 +35,7 @@ class DummyPacketInjector extends AbstractPacketInjector implements PacketInject
injector.getProxyPacketInjector().removePacketHandler(packet);
}
for (PacketType packet : added) {
injector.getProxyPacketInjector().addPacketHandler(packet);
injector.getProxyPacketInjector().addPacketHandler(packet, null);
}
}