diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/events/ListenerOptions.java b/ProtocolLib/src/main/java/com/comphenix/protocol/events/ListenerOptions.java index 224197e6..e38f32cb 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/events/ListenerOptions.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/events/ListenerOptions.java @@ -1,5 +1,7 @@ package com.comphenix.protocol.events; +import com.comphenix.protocol.injector.GamePhase; + /** * Represents additional options a listener may require. * @@ -10,4 +12,10 @@ public enum ListenerOptions { * Retrieve the serialized client packet as it appears on the network stream. */ INTERCEPT_INPUT_BUFFER, + + /** + * 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; } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/events/ListeningWhitelist.java b/ProtocolLib/src/main/java/com/comphenix/protocol/events/ListeningWhitelist.java index 61ab901c..f163ac2a 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/events/ListeningWhitelist.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/events/ListeningWhitelist.java @@ -143,7 +143,7 @@ public class ListeningWhitelist { public GamePhase getGamePhase() { return gamePhase; } - + /** * Retrieve every special option associated with this whitelist. * @return Every special option. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/LoginPackets.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/LoginPackets.java new file mode 100644 index 00000000..5178abef --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/LoginPackets.java @@ -0,0 +1,71 @@ +package com.comphenix.protocol.injector; + +import org.bukkit.Bukkit; + +import com.comphenix.protocol.Packets; +import com.comphenix.protocol.concurrency.IntegerSet; +import com.comphenix.protocol.events.ConnectionSide; +import com.comphenix.protocol.utility.MinecraftVersion; + +/** + * Packets that are known to be transmitted during login. + *
+ * This may be dynamically extended later.
+ * @author Kristian
+ */
+class LoginPackets {
+ private IntegerSet clientSide = new IntegerSet(Packets.PACKET_COUNT);
+ private IntegerSet serverSide = new IntegerSet(Packets.PACKET_COUNT);
+
+ public LoginPackets(MinecraftVersion version) {
+ // Ordinary login
+ clientSide.add(Packets.Client.HANDSHAKE);
+ serverSide.add(Packets.Server.KEY_REQUEST);
+ clientSide.add(Packets.Client.KEY_RESPONSE);
+ serverSide.add(Packets.Server.KEY_RESPONSE);
+ clientSide.add(Packets.Client.CLIENT_COMMAND);
+ serverSide.add(Packets.Server.LOGIN);
+
+ // List ping
+ clientSide.add(Packets.Client.GET_INFO);
+
+ // In 1.6.2, Minecraft started sending CUSTOM_PAYLOAD in the server list protocol
+ if (version.compareTo(MinecraftVersion.HORSE_UPDATE) >= 0) {
+ clientSide.add(Packets.Client.CUSTOM_PAYLOAD);
+ }
+ serverSide.add(Packets.Server.KICK_DISCONNECT);
+
+ // MCPC++ contains Forge, which uses packet 250 during login
+ if (isMCPC()) {
+ clientSide.add(Packets.Client.CUSTOM_PAYLOAD);
+ }
+ }
+
+ /**
+ * Determine if we are runnign MCPC.
+ * @return TRUE if we are, FALSE otherwise.
+ */
+ private static boolean isMCPC() {
+ return Bukkit.getServer().getVersion().contains("MCPC-Plus");
+ }
+
+ /**
+ * Determine if a packet may be sent during login from a given direction.
+ * @param packetId - the ID of the packet.
+ * @param side - the direction.
+ * @return TRUE if it may, FALSE otherwise.
+ */
+ public boolean isLoginPacket(int packetId, ConnectionSide side) {
+ switch (side) {
+ case CLIENT_SIDE:
+ return clientSide.contains(packetId);
+ case SERVER_SIDE:
+ return serverSide.contains(packetId);
+ case BOTH:
+ return clientSide.contains(packetId) ||
+ serverSide.contains(packetId);
+ default:
+ throw new IllegalArgumentException("Unknown connection side: " + side);
+ }
+ }
+}
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java
index 1f2ff123..140b82ce 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java
@@ -190,6 +190,9 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
// The current Minecraft version
private MinecraftVersion minecraftVersion;
+ // Login packets
+ private LoginPackets loginPackets;
+
/**
* Only create instances of this class if protocol lib is disabled.
*/
@@ -222,6 +225,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
// The plugin verifier
this.pluginVerifier = new PluginVerifier(builder.getLibrary());
this.minecraftVersion = builder.getMinecraftVersion();
+ this.loginPackets = new LoginPackets(minecraftVersion);
// The write packet interceptor
this.interceptWritePacket = new InterceptWritePacket(classLoader, reporter);
@@ -367,7 +371,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
playerInjection.checkListener(listener);
}
if (hasSending)
- incrementPhases(sending.getGamePhase());
+ incrementPhases(processPhase(sending, ConnectionSide.SERVER_SIDE));
// Handle receivers after senders
if (hasReceiving) {
@@ -376,7 +380,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
enablePacketFilters(listener, ConnectionSide.CLIENT_SIDE, receiving.getWhitelist());
}
if (hasReceiving)
- incrementPhases(receiving.getGamePhase());
+ incrementPhases(processPhase(receiving, ConnectionSide.CLIENT_SIDE));
// Inform our injected hooks
packetListeners.add(listener);
@@ -384,6 +388,20 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
}
}
+ private GamePhase processPhase(ListeningWhitelist whitelist, ConnectionSide side) {
+ // Determine if this is a login packet, ensuring that gamephase detection is enabled
+ if (!whitelist.getGamePhase().hasLogin() &&
+ !whitelist.getOptions().contains(ListenerOptions.DISABLE_GAMEPHASE_DETECTION)) {
+
+ for (int id : whitelist.getWhitelist()) {
+ if (loginPackets.isLoginPacket(id, side)) {
+ return GamePhase.BOTH;
+ }
+ }
+ }
+ return whitelist.getGamePhase();
+ }
+
/**
* Invoked when we need to update the input buffer set.
*/
@@ -483,11 +501,11 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
// Remove listeners and phases
if (sending != null && sending.isEnabled()) {
sendingRemoved = sendingListeners.removeListener(listener, sending);
- decrementPhases(sending.getGamePhase());
+ decrementPhases(processPhase(sending, ConnectionSide.SERVER_SIDE));
}
if (receiving != null && receiving.isEnabled()) {
receivingRemoved = recievedListeners.removeListener(listener, receiving);
- decrementPhases(receiving.getGamePhase());
+ decrementPhases(processPhase(receiving, ConnectionSide.CLIENT_SIDE));
}
// Remove hooks, if needed
@@ -500,7 +518,6 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
@Override
public void removePacketListeners(Plugin plugin) {
-
// Iterate through every packet listener
for (PacketListener listener : packetListeners) {
// Remove the listener
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/packet/ProxyPacketInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/packet/ProxyPacketInjector.java
index 22ef72dc..9cce756b 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/packet/ProxyPacketInjector.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/packet/ProxyPacketInjector.java
@@ -32,7 +32,6 @@ import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.NoOp;
-import com.comphenix.protocol.Packets;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
@@ -57,7 +56,7 @@ import com.comphenix.protocol.wrappers.WrappedIntHashMap;
*/
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("Unknown origin %s for packet %s. Are you using GamePhase.LOGIN?");
+ 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.
@@ -327,9 +326,8 @@ class ProxyPacketInjector implements PacketInjector {
if (client != null) {
return packetRecieved(packet, client, buffered);
} else {
- // Hack #2 - Caused by our server socket injector
- if (packet.getID() != Packets.Client.GET_INFO)
- reporter.reportWarning(this, Report.newBuilder(REPORT_UNKNOWN_ORIGIN_FOR_PACKET).messageParam(input, packet.getID()));
+ // The timeout elapsed!
+ reporter.reportWarning(this, Report.newBuilder(REPORT_UNKNOWN_ORIGIN_FOR_PACKET).messageParam(input, packet.getID()));
return null;
}
diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftVersion.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftVersion.java
index 5f9ccc28..24f33e54 100644
--- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftVersion.java
+++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftVersion.java
@@ -40,6 +40,26 @@ public class MinecraftVersion implements Comparable