package net.minecraft.server;
// CraftBukkit start
import java.net.InetAddress;
// CraftBukkit end
import java.util.Map;
import com.koloboke.collect.map.hash.HashObjLongMaps;
public class HandshakeListener implements PacketHandshakingInListener {
private static final com.google.gson.Gson gson = new com.google.gson.Gson(); // Spigot
// CraftBukkit start - add fields
private static final Map<InetAddress, Long> throttleTracker = HashObjLongMaps.newMutableMap();
private static int throttleCounter = 0;
// CraftBukkit end
private final MinecraftServer a;
private final NetworkManager b;
public HandshakeListener(MinecraftServer minecraftserver, NetworkManager networkmanager) {
this.a = minecraftserver;
this.b = networkmanager;
}
@Override
public void a(PacketHandshakingInSetProtocol packethandshakinginsetprotocol) {
switch (packethandshakinginsetprotocol.a()) {
case LOGIN:
this.b.setProtocol(EnumProtocol.LOGIN);
ChatComponentText chatcomponenttext;
// CraftBukkit start - Connection throttle
try {
long currentTime = System.currentTimeMillis();
long connectionThrottle = MinecraftServer.getServer().server.getConnectionThrottle();
InetAddress address = ((java.net.InetSocketAddress) this.b.getSocketAddress()).getAddress();
synchronized (throttleTracker) {
if (throttleTracker.containsKey(address) && !"127.0.0.1".equals(address.getHostAddress()) && currentTime - throttleTracker.get(address) < connectionThrottle) {
throttleTracker.put(address, currentTime);
chatcomponenttext = new ChatComponentText("Connection throttled! Please wait before reconnecting.");
this.b.sendPacket(new PacketLoginOutDisconnect(chatcomponenttext));
this.b.close(chatcomponenttext);
return;
}
throttleTracker.put(address, currentTime);
throttleCounter++;
if (throttleCounter > 200) {
throttleCounter = 0;
// Cleanup stale entries
java.util.Iterator iter = throttleTracker.entrySet().iterator();
while (iter.hasNext()) {
java.util.Map.Entry<InetAddress, Long> entry = (java.util.Map.Entry) iter.next();
if (entry.getValue() > connectionThrottle) {
iter.remove();
}
}
}
}
} catch (Throwable t) {
org.apache.logging.log4j.LogManager.getLogger().debug("Failed to check connection throttle", t);
}
// CraftBukkit end
if (packethandshakinginsetprotocol.b() > 316) {
chatcomponenttext = new ChatComponentText( java.text.MessageFormat.format( org.spigotmc.SpigotConfig.outdatedServerMessage.replaceAll("'", "''"), "1.11.2" ) ); // Spigot
this.b.sendPacket(new PacketLoginOutDisconnect(chatcomponenttext));
this.b.close(chatcomponenttext);
} else if (packethandshakinginsetprotocol.b() < 316) {
chatcomponenttext = new ChatComponentText( java.text.MessageFormat.format( org.spigotmc.SpigotConfig.outdatedClientMessage.replaceAll("'", "''"), "1.11.2" ) ); // Spigot
this.b.sendPacket(new PacketLoginOutDisconnect(chatcomponenttext));
this.b.close(chatcomponenttext);
} else {
this.b.setPacketListener(new LoginListener(this.a, this.b));
// Paper start - handshake event
boolean proxyLogicEnabled = org.spigotmc.SpigotConfig.bungee;
boolean handledByEvent = false;
// Try and handle the handshake through the event
if (com.destroystokyo.paper.event.player.PlayerHandshakeEvent.getHandlerList().getRegisteredListeners().length != 0) { // Hello? Can you hear me?
com.destroystokyo.paper.event.player.PlayerHandshakeEvent event = new com.destroystokyo.paper.event.player.PlayerHandshakeEvent(packethandshakinginsetprotocol.hostname, !proxyLogicEnabled);
if (event.callEvent()) {
// If we've failed somehow, let the client know so and go no further.
if (event.isFailed()) {
chatcomponenttext = new ChatComponentText(event.getFailMessage());
this.b.sendPacket(new PacketLoginOutDisconnect(chatcomponenttext));
this.b.close(chatcomponenttext);
return;
}
packethandshakinginsetprotocol.hostname = event.getServerHostname();
this.b.l = new java.net.InetSocketAddress(event.getSocketAddressHostname(), ((java.net.InetSocketAddress) this.b.getSocketAddress()).getPort());
this.b.spoofedUUID = event.getUniqueId();
this.b.spoofedProfile = gson.fromJson(event.getPropertiesJson(), com.mojang.authlib.properties.Property[].class);
handledByEvent = true; // Hooray, we did it!
}
}
// Don't try and handle default logic if it's been handled by the event.
if (!handledByEvent && proxyLogicEnabled) {
// Paper end
// Spigot Start
//if (org.spigotmc.SpigotConfig.bungee) { // Paper - comment out, we check above!
String[] split = packethandshakinginsetprotocol.hostname.split("\00");
if ( split.length == 3 || split.length == 4 ) {
packethandshakinginsetprotocol.hostname = split[0];
b.l = new java.net.InetSocketAddress(split[1], ((java.net.InetSocketAddress) b.getSocketAddress()).getPort());
b.spoofedUUID = com.mojang.util.UUIDTypeAdapter.fromString( split[2] );
} else
{
chatcomponenttext = new ChatComponentText("If you wish to use IP forwarding, please enable it in your BungeeCord config as well!");
this.b.sendPacket(new PacketLoginOutDisconnect(chatcomponenttext));
this.b.close(chatcomponenttext);
return;
}
if ( split.length == 4 )
{
b.spoofedProfile = gson.fromJson(split[3], com.mojang.authlib.properties.Property[].class);
}
}
// Spigot End
((LoginListener) this.b.i()).hostname = packethandshakinginsetprotocol.hostname + ":" + packethandshakinginsetprotocol.port; // CraftBukkit - set hostname
}
break;
case STATUS:
this.b.setProtocol(EnumProtocol.STATUS);
this.b.setPacketListener(new PacketStatusListener(this.a, this.b));
break;
default:
throw new UnsupportedOperationException("Invalid intention " + packethandshakinginsetprotocol.a());
}
}
@Override
public void a(IChatBaseComponent ichatbasecomponent) {}
}