package net.md_5.bungee.entitymap; import com.flowpowered.nbt.stream.NBTInputStream; import com.google.common.base.Throwables; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; import java.io.IOException; import lombok.AccessLevel; import lombok.NoArgsConstructor; import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.ProtocolConstants; /** * Class to rewrite integers within packets. */ @NoArgsConstructor(access = AccessLevel.PACKAGE) public abstract class EntityMap { private final boolean[] clientboundInts = new boolean[ 256 ]; private final boolean[] clientboundVarInts = new boolean[ 256 ]; private final boolean[] serverboundInts = new boolean[ 256 ]; private final boolean[] serverboundVarInts = new boolean[ 256 ]; // Returns the correct entity map for the protocol version public static EntityMap getEntityMap(int version) { switch ( version ) { case ProtocolConstants.MINECRAFT_1_8: return EntityMap_1_8.INSTANCE; case ProtocolConstants.MINECRAFT_1_9: case ProtocolConstants.MINECRAFT_1_9_1: case ProtocolConstants.MINECRAFT_1_9_2: return EntityMap_1_9.INSTANCE; case ProtocolConstants.MINECRAFT_1_9_4: return EntityMap_1_9_4.INSTANCE; case ProtocolConstants.MINECRAFT_1_10: return EntityMap_1_10.INSTANCE; case ProtocolConstants.MINECRAFT_1_11: case ProtocolConstants.MINECRAFT_1_11_1: return EntityMap_1_11.INSTANCE; case ProtocolConstants.MINECRAFT_1_12: return EntityMap_1_12.INSTANCE; } throw new RuntimeException( "Version " + version + " has no entity map" ); } protected void addRewrite(int id, ProtocolConstants.Direction direction, boolean varint) { if ( direction == ProtocolConstants.Direction.TO_CLIENT ) { if ( varint ) { clientboundVarInts[id] = true; } else { clientboundInts[id] = true; } } else if ( varint ) { serverboundVarInts[id] = true; } else { serverboundInts[id] = true; } } public void rewriteServerbound(ByteBuf packet, int oldId, int newId) { rewrite( packet, oldId, newId, serverboundInts, serverboundVarInts ); } public void rewriteClientbound(ByteBuf packet, int oldId, int newId) { rewrite( packet, oldId, newId, clientboundInts, clientboundVarInts ); } protected static void rewriteInt(ByteBuf packet, int oldId, int newId, int offset) { int readId = packet.getInt( offset ); if ( readId == oldId ) { packet.setInt( offset, newId ); } else if ( readId == newId ) { packet.setInt( offset, oldId ); } } @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") protected static void rewriteVarInt(ByteBuf packet, int oldId, int newId, int offset) { // Need to rewrite the packet because VarInts are variable length int readId = DefinedPacket.readVarInt( packet ); int readIdLength = packet.readerIndex() - offset; if ( readId == oldId || readId == newId ) { ByteBuf data = packet.copy(); packet.readerIndex( offset ); packet.writerIndex( offset ); DefinedPacket.writeVarInt( readId == oldId ? newId : oldId, packet ); packet.writeBytes( data ); data.release(); } } protected static void rewriteMetaVarInt(ByteBuf packet, int oldId, int newId, int metaIndex) { int readerIndex = packet.readerIndex(); short index; while ( ( index = packet.readUnsignedByte() ) != 0xFF ) { int type = DefinedPacket.readVarInt( packet ); switch ( type ) { case 0: packet.skipBytes( 1 ); // byte break; case 1: if ( index == metaIndex ) { int position = packet.readerIndex(); rewriteVarInt( packet, oldId, newId, position ); packet.readerIndex( position ); } DefinedPacket.readVarInt( packet ); break; case 2: packet.skipBytes( 4 ); // float break; case 3: case 4: DefinedPacket.readString( packet ); break; case 5: if ( packet.readShort() != -1 ) { packet.skipBytes( 3 ); // byte, short int position = packet.readerIndex(); if ( packet.readByte() != 0 ) { packet.readerIndex( position ); try { new NBTInputStream( new ByteBufInputStream( packet ), false ).readTag(); } catch ( IOException ex ) { throw Throwables.propagate( ex ); } } } break; case 6: packet.skipBytes( 1 ); // boolean break; case 7: packet.skipBytes( 12 ); // float, float, float break; case 8: packet.readLong(); break; case 9: if ( packet.readBoolean() ) { packet.skipBytes( 8 ); // long } break; case 10: DefinedPacket.readVarInt( packet ); break; case 11: if ( packet.readBoolean() ) { packet.skipBytes( 16 ); // long, long } break; case 12: DefinedPacket.readVarInt( packet ); break; case 13: try { new NBTInputStream( new ByteBufInputStream( packet ), false ).readTag(); } catch ( IOException ex ) { throw Throwables.propagate( ex ); } break; default: throw new IllegalArgumentException( "Unknown meta type " + type ); } } packet.readerIndex( readerIndex ); } // Handles simple packets private static void rewrite(ByteBuf packet, int oldId, int newId, boolean[] ints, boolean[] varints) { int readerIndex = packet.readerIndex(); int packetId = DefinedPacket.readVarInt( packet ); int packetIdLength = packet.readerIndex() - readerIndex; if ( ints[packetId] ) { rewriteInt( packet, oldId, newId, readerIndex + packetIdLength ); } else if ( varints[packetId] ) { rewriteVarInt( packet, oldId, newId, readerIndex + packetIdLength ); } packet.readerIndex( readerIndex ); } }