package net.md_5.bungee.protocol;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.TObjectIntMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.map.hash.TObjectIntHashMap;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.protocol.packet.BossBar;
import net.md_5.bungee.protocol.packet.Chat;
import net.md_5.bungee.protocol.packet.ClientSettings;
import net.md_5.bungee.protocol.packet.EncryptionRequest;
import net.md_5.bungee.protocol.packet.EncryptionResponse;
import net.md_5.bungee.protocol.packet.Handshake;
import net.md_5.bungee.protocol.packet.KeepAlive;
import net.md_5.bungee.protocol.packet.Kick;
import net.md_5.bungee.protocol.packet.Login;
import net.md_5.bungee.protocol.packet.LoginRequest;
import net.md_5.bungee.protocol.packet.LoginSuccess;
import net.md_5.bungee.protocol.packet.PingPacket;
import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter;
import net.md_5.bungee.protocol.packet.PlayerListItem;
import net.md_5.bungee.protocol.packet.PluginMessage;
import net.md_5.bungee.protocol.packet.Respawn;
import net.md_5.bungee.protocol.packet.ScoreboardDisplay;
import net.md_5.bungee.protocol.packet.ScoreboardObjective;
import net.md_5.bungee.protocol.packet.ScoreboardScore;
import net.md_5.bungee.protocol.packet.SetCompression;
import net.md_5.bungee.protocol.packet.StatusRequest;
import net.md_5.bungee.protocol.packet.StatusResponse;
import net.md_5.bungee.protocol.packet.TabCompleteRequest;
import net.md_5.bungee.protocol.packet.TabCompleteResponse;
import net.md_5.bungee.protocol.packet.Team;
import net.md_5.bungee.protocol.packet.Title;
public enum Protocol
{
// Undef
HANDSHAKE
{
{
TO_SERVER.registerPacket(
Handshake.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x00 )
);
}
},
// 0
GAME
{
{
TO_CLIENT.registerPacket(
KeepAlive.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x00 ),
map( ProtocolConstants.MINECRAFT_1_9, 0x1F ),
map( ProtocolConstants.MINECRAFT_1_12, 0x1F )
);
TO_CLIENT.registerPacket(
Login.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x01 ),
map( ProtocolConstants.MINECRAFT_1_9, 0x23 ),
map( ProtocolConstants.MINECRAFT_1_12, 0x23 )
);
TO_CLIENT.registerPacket(
Chat.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x02 ),
map( ProtocolConstants.MINECRAFT_1_9, 0x0F ),
map( ProtocolConstants.MINECRAFT_1_12, 0x0F )
);
TO_CLIENT.registerPacket(
Respawn.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x07 ),
map( ProtocolConstants.MINECRAFT_1_9, 0x33 ),
map( ProtocolConstants.MINECRAFT_1_12, 0x34 )
);
TO_CLIENT.registerPacket(
BossBar.class,
map( ProtocolConstants.MINECRAFT_1_9, 0x0C ),
map( ProtocolConstants.MINECRAFT_1_12, 0x0C )
);
TO_CLIENT.registerPacket(
PlayerListItem.class, // PlayerInfo
map( ProtocolConstants.MINECRAFT_1_8, 0x38 ),
map( ProtocolConstants.MINECRAFT_1_9, 0x2D ),
map( ProtocolConstants.MINECRAFT_1_12, 0x2D )
);
TO_CLIENT.registerPacket(
TabCompleteResponse.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x3A ),
map( ProtocolConstants.MINECRAFT_1_9, 0x0E ),
map( ProtocolConstants.MINECRAFT_1_12, 0x0E )
);
TO_CLIENT.registerPacket(
ScoreboardObjective.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x3B ),
map( ProtocolConstants.MINECRAFT_1_9, 0x3F ),
map( ProtocolConstants.MINECRAFT_1_12, 0x41 )
);
TO_CLIENT.registerPacket(
ScoreboardScore.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x3C ),
map( ProtocolConstants.MINECRAFT_1_9, 0x42 ),
map( ProtocolConstants.MINECRAFT_1_12, 0x44 )
);
TO_CLIENT.registerPacket(
ScoreboardDisplay.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x3D ),
map( ProtocolConstants.MINECRAFT_1_9, 0x38 ),
map( ProtocolConstants.MINECRAFT_1_12, 0x3A )
);
TO_CLIENT.registerPacket(
Team.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x3E ),
map( ProtocolConstants.MINECRAFT_1_9, 0x41 ),
map( ProtocolConstants.MINECRAFT_1_12, 0x43 )
);
TO_CLIENT.registerPacket(
PluginMessage.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x3F ),
map( ProtocolConstants.MINECRAFT_1_9, 0x18 ),
map( ProtocolConstants.MINECRAFT_1_12, 0x18 )
);
TO_CLIENT.registerPacket(
Kick.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x40 ),
map( ProtocolConstants.MINECRAFT_1_9, 0x1A ),
map( ProtocolConstants.MINECRAFT_1_12, 0x1A )
);
TO_CLIENT.registerPacket(
Title.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x45 ),
map( ProtocolConstants.MINECRAFT_1_12, 0x47 )
);
TO_CLIENT.registerPacket(
PlayerListHeaderFooter.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x47 ),
map( ProtocolConstants.MINECRAFT_1_9, 0x48 ),
map( ProtocolConstants.MINECRAFT_1_9_4, 0x47 ),
map( ProtocolConstants.MINECRAFT_1_12, 0x49 )
);
TO_SERVER.registerPacket(
KeepAlive.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x00 ),
map( ProtocolConstants.MINECRAFT_1_9, 0x0B ),
map( ProtocolConstants.MINECRAFT_1_12, 0x0C )
);
TO_SERVER.registerPacket(
Chat.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x01 ),
map( ProtocolConstants.MINECRAFT_1_9, 0x02 ),
map( ProtocolConstants.MINECRAFT_1_12, 0x03 )
);
TO_SERVER.registerPacket(
TabCompleteRequest.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x14 ),
map( ProtocolConstants.MINECRAFT_1_9, 0x01 ),
map( ProtocolConstants.MINECRAFT_1_12, 0x02 )
);
TO_SERVER.registerPacket(
ClientSettings.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x15 ),
map( ProtocolConstants.MINECRAFT_1_9, 0x04 ),
map( ProtocolConstants.MINECRAFT_1_12, 0x05 )
);
TO_SERVER.registerPacket(
PluginMessage.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x17 ),
map( ProtocolConstants.MINECRAFT_1_9, 0x09 ),
map( ProtocolConstants.MINECRAFT_1_12, 0x0A )
);
}
},
// 1
STATUS
{
{
TO_CLIENT.registerPacket(
StatusResponse.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x00 )
);
TO_CLIENT.registerPacket(
PingPacket.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x01 )
);
TO_SERVER.registerPacket(
StatusRequest.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x00 )
);
TO_SERVER.registerPacket(
PingPacket.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x01 )
);
}
},
//2
LOGIN
{
{
TO_CLIENT.registerPacket(
Kick.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x00 )
);
TO_CLIENT.registerPacket(
EncryptionRequest.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x01 )
);
TO_CLIENT.registerPacket(
LoginSuccess.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x02 )
);
TO_CLIENT.registerPacket(
SetCompression.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x03 )
);
TO_SERVER.registerPacket(
LoginRequest.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x00 )
);
TO_SERVER.registerPacket(
EncryptionResponse.class,
map( ProtocolConstants.MINECRAFT_1_8, 0x01 )
);
}
};
/*========================================================================*/
public static final int MAX_PACKET_ID = 0xFF;
/*========================================================================*/
public final DirectionData TO_SERVER = new DirectionData(this, ProtocolConstants.Direction.TO_SERVER );
public final DirectionData TO_CLIENT = new DirectionData(this, ProtocolConstants.Direction.TO_CLIENT );
public static void main(String[] args)
{
for ( int version : ProtocolConstants.SUPPORTED_VERSION_IDS )
{
dump( version );
}
}
private static void dump(int version)
{
for ( Protocol protocol : Protocol.values() )
{
dump( version, protocol );
}
}
private static void dump(int version, Protocol protocol)
{
dump( version, protocol.TO_CLIENT );
dump( version, protocol.TO_SERVER );
}
private static void dump(int version, DirectionData data)
{
for ( int id = 0; id < MAX_PACKET_ID; id++ )
{
DefinedPacket packet = data.createPacket( id, version );
if ( packet != null )
{
System.out.println( version + " " + data.protocolPhase + " " + data.direction + " " + id + " " + packet.getClass().getSimpleName() );
}
}
}
@RequiredArgsConstructor
private static class ProtocolData {
private final int protocolVersion;
private final TObjectIntMap<Class<? extends DefinedPacket>> packetMap = new TObjectIntHashMap<>( MAX_PACKET_ID );
private final Constructor<? extends DefinedPacket>[] packetConstructors = new Constructor[ MAX_PACKET_ID ];
}
@RequiredArgsConstructor
private static class ProtocolMapping {
private final int protocolVersion;
private final int packetID;
}
// Helper method
private static ProtocolMapping map(int protocol, int id) {
return new ProtocolMapping(protocol, id);
}
@RequiredArgsConstructor
public static class DirectionData
{
private final Protocol protocolPhase;
private final TIntObjectMap<ProtocolData> protocols = new TIntObjectHashMap<>();
{
for ( int protocol : ProtocolConstants.SUPPORTED_VERSION_IDS )
{
protocols.put( protocol, new ProtocolData( protocol ) );
}
}
private final TIntObjectMap<List<Integer>> linkedProtocols = new TIntObjectHashMap<>();
{
linkedProtocols.put( ProtocolConstants.MINECRAFT_1_8, Arrays.asList(
ProtocolConstants.MINECRAFT_1_9,
ProtocolConstants.MINECRAFT_1_12
) );
linkedProtocols.put( ProtocolConstants.MINECRAFT_1_9, Arrays.asList(
ProtocolConstants.MINECRAFT_1_9_1,
ProtocolConstants.MINECRAFT_1_9_2,
ProtocolConstants.MINECRAFT_1_9_4
) );
linkedProtocols.put( ProtocolConstants.MINECRAFT_1_9_4, Arrays.asList(
ProtocolConstants.MINECRAFT_1_10,
ProtocolConstants.MINECRAFT_1_11,
ProtocolConstants.MINECRAFT_1_11_1
) );
}
@Getter
private final ProtocolConstants.Direction direction;
private ProtocolData getProtocolData(int version)
{
ProtocolData protocol = protocols.get( version );
if ( protocol == null && ( protocolPhase != Protocol.GAME ) )
{
protocol = Iterables.getFirst( protocols.valueCollection(), null );
}
return protocol;
}
public final DefinedPacket createPacket(int id, int version)
{
ProtocolData protocolData = getProtocolData( version );
if (protocolData == null)
{
throw new BadPacketException( "Unsupported protocol version" );
}
if ( id > MAX_PACKET_ID )
{
throw new BadPacketException( "Packet with id " + id + " outside of range " );
}
Constructor<? extends DefinedPacket> constructor = protocolData.packetConstructors[id];
try
{
return ( constructor == null ) ? null : constructor.newInstance();
} catch ( ReflectiveOperationException ex )
{
throw new BadPacketException( "Could not construct packet with id " + id, ex );
}
}
protected final void registerPacket(Class<? extends DefinedPacket> packetClass, ProtocolMapping ...mappings)
{
try
{
Constructor<? extends DefinedPacket> constructor = packetClass.getDeclaredConstructor();
for ( ProtocolMapping mapping : mappings )
{
ProtocolData data = protocols.get( mapping.protocolVersion );
data.packetMap.put( packetClass, mapping.packetID );
data.packetConstructors[mapping.packetID] = constructor;
List<Integer> links = linkedProtocols.get( mapping.protocolVersion );
if ( links != null )
{
links: for ( int link : links )
{
// Check for manual mappings
for ( ProtocolMapping m : mappings )
{
if ( m == mapping ) continue;
if ( m.protocolVersion == link ) continue links;
List<Integer> innerLinks = linkedProtocols.get( m.protocolVersion );
if ( innerLinks != null && innerLinks.contains( link ) ) continue links;
}
registerPacket( packetClass, map( link, mapping.packetID ) );
}
}
}
} catch ( NoSuchMethodException ex )
{
throw new BadPacketException( "No NoArgsConstructor for packet class " + packetClass );
}
}
final int getId(Class<? extends DefinedPacket> packet, int version)
{
ProtocolData protocolData = getProtocolData( version );
if (protocolData == null)
{
throw new BadPacketException( "Unsupported protocol version" );
}
Preconditions.checkArgument( protocolData.packetMap.containsKey( packet ), "Cannot get ID for packet " + packet );
return protocolData.packetMap.get( packet );
}
}
}