    001    package cpw.mods.fml.common.network;
    002
    003    import static cpw.mods.fml.common.network.FMLPacket.Type.MOD_LIST_REQUEST;
    004
    005    import java.io.IOException;
    006    import java.net.InetAddress;
    007    import java.net.NetworkInterface;
    008    import java.net.SocketAddress;
    009    import java.util.Collections;
    010    import java.util.List;
    011    import java.util.Map;
    012    import java.util.Set;
    013
    014    import net.minecraft.entity.Entity;
    015    import net.minecraft.entity.player.EntityPlayer;
    016    import net.minecraft.entity.player.EntityPlayerMP;
    017    import net.minecraft.item.Item;
    018    import net.minecraft.network.*;
    019    import net.minecraft.network.packet.*;
    020    import net.minecraft.server.MinecraftServer;
    021    import net.minecraft.server.management.ServerConfigurationManager;
    022    import net.minecraft.world.EnumGameType;
    023    import net.minecraft.world.World;
    024    import net.minecraft.world.WorldType;
    025
    026    import com.google.common.collect.Lists;
    027    import com.google.common.collect.Maps;
    028    import com.google.common.hash.Hashing;
    029
    030    import cpw.mods.fml.common.FMLCommonHandler;
    031    import cpw.mods.fml.common.FMLLog;
    032    import cpw.mods.fml.common.Loader;
    033    import cpw.mods.fml.common.ModContainer;
    034    import cpw.mods.fml.common.discovery.ASMDataTable;
    035    import cpw.mods.fml.common.network.FMLPacket.Type;
    036    import cpw.mods.fml.common.registry.EntityRegistry;
    037    import cpw.mods.fml.common.registry.GameRegistry;
    038    import cpw.mods.fml.common.registry.EntityRegistry.EntityRegistration;
    039
    040    public class FMLNetworkHandler
    041    {
    042        private static final int FML_HASH = Hashing.murmur3_32().hashString("FML").asInt();
    043        private static final int PROTOCOL_VERSION = 0x2;
    044        private static final FMLNetworkHandler INSTANCE = new FMLNetworkHandler();
    045
    046        // List of states for connections from clients to server
    047        static final int LOGIN_RECEIVED = 1;
    048        static final int CONNECTION_VALID = 2;
    049        static final int FML_OUT_OF_DATE = -1;
    050        static final int MISSING_MODS_OR_VERSIONS = -2;
    051
    052        private Map<NetLoginHandler, Integer> loginStates = Maps.newHashMap();
    053        private Map<ModContainer, NetworkModHandler> networkModHandlers = Maps.newHashMap();
    054
    055        private Map<Integer, NetworkModHandler> networkIdLookup = Maps.newHashMap();
    056
    057        public static void handlePacket250Packet(Packet250CustomPayload packet, INetworkManager network, NetHandler handler)
    058        {
    059            String target = packet.channel;
    060
    061            if (target.startsWith("MC|"))
    062            {
    063                handler.handleVanilla250Packet(packet);
    064            }
    065            if (target.equals("FML"))
    066            {
    067                instance().handleFMLPacket(packet, network, handler);
    068            }
    069            else
    070            {
    071                NetworkRegistry.instance().handleCustomPacket(packet, network, handler);
    072            }
    073        }
    074
    075        public static void onConnectionEstablishedToServer(NetHandler clientHandler, INetworkManager manager, Packet1Login login)
    076        {
    077            NetworkRegistry.instance().clientLoggedIn(clientHandler, manager, login);
    078        }
    079
    080        private void handleFMLPacket(Packet250CustomPayload packet, INetworkManager network, NetHandler netHandler)
    081        {
    082            FMLPacket pkt = FMLPacket.readPacket(network, packet.data);
    083            // Part of an incomplete multipart packet
    084            if (pkt == null)
    085            {
    086                return;
    087            }
    088            String userName = "";
    089            if (netHandler instanceof NetLoginHandler)
    090            {
    091                userName = ((NetLoginHandler) netHandler).clientUsername;
    092            }
    093            else
    094            {
    095                EntityPlayer pl = netHandler.getPlayer();
    096                if (pl != null)
    097                {
    098                    userName = pl.getCommandSenderName();
    099                }
    100            }
    101
    102            pkt.execute(network, this, netHandler, userName);
    103        }
    104
    105        public static void onConnectionReceivedFromClient(NetLoginHandler netLoginHandler, MinecraftServer server, SocketAddress address, String userName)
    106        {
    107            instance().handleClientConnection(netLoginHandler, server, address, userName);
    108        }
    109
    110        private void handleClientConnection(NetLoginHandler netLoginHandler, MinecraftServer server, SocketAddress address, String userName)
    111        {
    112            if (!loginStates.containsKey(netLoginHandler))
    113            {
    114                if (handleVanillaLoginKick(netLoginHandler, server, address, userName))
    115                {
    116                    // No FML on the client
    117                    FMLLog.fine("Connection from %s rejected - no FML packet received from client", userName);
    118                    netLoginHandler.completeConnection("You don't have FML installed, you cannot connect to this server");
    119                    return;
    120                }
    121                else
    122                {
    123                    // Vanilla kicked us for some reason - bye now!
    124                    FMLLog.fine("Connection from %s was closed by vanilla minecraft", userName);
    125                    return;
    126                }
    127
    128            }
    129            switch (loginStates.get(netLoginHandler))
    130            {
    131            case LOGIN_RECEIVED:
    132                // mods can try and kick undesireables here
    133                String modKick = NetworkRegistry.instance().connectionReceived(netLoginHandler, netLoginHandler.myTCPConnection);
    134                if (modKick != null)
    135                {
    136                    netLoginHandler.completeConnection(modKick);
    137                    loginStates.remove(netLoginHandler);
    138                    return;
    139                }
    140                // The vanilla side wanted to kick
    141                if (!handleVanillaLoginKick(netLoginHandler, server, address, userName))
    142                {
    143                    loginStates.remove(netLoginHandler);
    144                    return;
    145                }
    146                // Reset the "connection completed" flag so processing can continue
    147                NetLoginHandler.func_72531_a(netLoginHandler, false);
    148                // Send the mod list request packet to the client from the server
    149                netLoginHandler.myTCPConnection.addToSendQueue(getModListRequestPacket());
    150                loginStates.put(netLoginHandler, CONNECTION_VALID);
    151                break;
    152            case CONNECTION_VALID:
    153                netLoginHandler.completeConnection(null);
    154                loginStates.remove(netLoginHandler);
    155                break;
    156            case MISSING_MODS_OR_VERSIONS:
    157                netLoginHandler.completeConnection("The server requires mods that are absent or out of date on your client");
    158                loginStates.remove(netLoginHandler);
    159                break;
    160            case FML_OUT_OF_DATE:
    161                netLoginHandler.completeConnection("Your client is not running a new enough version of FML to connect to this server");
    162                loginStates.remove(netLoginHandler);
    163                break;
    164            default:
    165                netLoginHandler.completeConnection("There was a problem during FML negotiation");
    166                loginStates.remove(netLoginHandler);
    167                break;
    168            }
    169        }
    170
    171        /**
    172         * @param netLoginHandler
    173         * @param server
    174         * @param address
    175         * @param userName
    176         * @return if the user can carry on
    177         */
    178        private boolean handleVanillaLoginKick(NetLoginHandler netLoginHandler, MinecraftServer server, SocketAddress address, String userName)
    179        {
    180            // Vanilla reasons first
    181            ServerConfigurationManager playerList = server.getConfigurationManager();
    182            String kickReason = playerList.allowUserToConnect(address, userName);
    183
    184            if (kickReason != null)
    185            {
    186                netLoginHandler.completeConnection(kickReason);
    187            }
    188            return kickReason == null;
    189        }
    190
    191        public static void handleLoginPacketOnServer(NetLoginHandler handler, Packet1Login login)
    192        {
    193            if (login.clientEntityId == FML_HASH)
    194            {
    195                if (login.dimension == PROTOCOL_VERSION)
    196                {
    197                    FMLLog.finest("Received valid FML login packet from %s", handler.myTCPConnection.getSocketAddress());
    198                    instance().loginStates.put(handler, LOGIN_RECEIVED);
    199                }
    200                else if (login.dimension != PROTOCOL_VERSION)
    201                {
    202                    FMLLog.finest("Received incorrect FML (%x) login packet from %s", login.dimension, handler.myTCPConnection.getSocketAddress());
    203                    instance().loginStates.put(handler, FML_OUT_OF_DATE);
    204                }
    205            }
    206            else
    207            {
    208                FMLLog.fine("Received invalid login packet (%x, %x) from %s", login.clientEntityId, login.dimension,
    209                        handler.myTCPConnection.getSocketAddress());
    210            }
    211        }
    212
    213        static void setHandlerState(NetLoginHandler handler, int state)
    214        {
    215            instance().loginStates.put(handler, state);
    216        }
    217
    218        public static FMLNetworkHandler instance()
    219        {
    220            return INSTANCE;
    221        }
    222
    223        public static Packet1Login getFMLFakeLoginPacket()
    224        {
    225            // Always reset compat to zero before sending our fake packet
    226            FMLCommonHandler.instance().getSidedDelegate().setClientCompatibilityLevel((byte) 0);
    227            Packet1Login fake = new Packet1Login();
    228            // Hash FML using a simple function
    229            fake.clientEntityId = FML_HASH;
    230            // The FML protocol version
    231            fake.dimension = PROTOCOL_VERSION;
    232            fake.gameType = EnumGameType.NOT_SET;
    233            fake.terrainType = WorldType.worldTypes[0];
    234            return fake;
    235        }
    236
    237        public Packet250CustomPayload getModListRequestPacket()
    238        {
    239            return PacketDispatcher.getPacket("FML", FMLPacket.makePacket(MOD_LIST_REQUEST));
    240        }
    241
    242        public void registerNetworkMod(NetworkModHandler handler)
    243        {
    244            networkModHandlers.put(handler.getContainer(), handler);
    245            networkIdLookup.put(handler.getNetworkId(), handler);
    246        }
    247        public boolean registerNetworkMod(ModContainer container, Class<?> networkModClass, ASMDataTable asmData)
    248        {
    249            NetworkModHandler handler = new NetworkModHandler(container, networkModClass, asmData);
    250            if (handler.isNetworkMod())
    251            {
    252                registerNetworkMod(handler);
    253            }
    254
    255            return handler.isNetworkMod();
    256        }
    257
    258        public NetworkModHandler findNetworkModHandler(Object mc)
    259        {
    260            if (mc instanceof ModContainer)
    261            {
    262                return networkModHandlers.get(mc);
    263            }
    264            else if (mc instanceof Integer)
    265            {
    266                return networkIdLookup.get(mc);
    267            }
    268            else
    269            {
    270                return networkModHandlers.get(FMLCommonHandler.instance().findContainerFor(mc));
    271            }
    272        }
    273
    274        public Set<ModContainer> getNetworkModList()
    275        {
    276            return networkModHandlers.keySet();
    277        }
    278
    279        public static void handlePlayerLogin(EntityPlayerMP player, NetServerHandler netHandler, INetworkManager manager)
    280        {
    281            NetworkRegistry.instance().playerLoggedIn(player, netHandler, manager);
    282            GameRegistry.onPlayerLogin(player);
    283        }
    284
    285        public Map<Integer, NetworkModHandler> getNetworkIdMap()
    286        {
    287            return networkIdLookup;
    288        }
    289
    290        public void bindNetworkId(String key, Integer value)
    291        {
    292            Map<String, ModContainer> mods = Loader.instance().getIndexedModList();
    293            NetworkModHandler handler = findNetworkModHandler(mods.get(key));
    294            if (handler != null)
    295            {
    296                handler.setNetworkId(value);
    297                networkIdLookup.put(value, handler);
    298            }
    299        }
    300
    301        public static void onClientConnectionToRemoteServer(NetHandler netClientHandler, String server, int port, INetworkManager networkManager)
    302        {
    303            NetworkRegistry.instance().connectionOpened(netClientHandler, server, port, networkManager);
    304        }
    305
    306        public static void onClientConnectionToIntegratedServer(NetHandler netClientHandler, MinecraftServer server, INetworkManager networkManager)
    307        {
    308            NetworkRegistry.instance().connectionOpened(netClientHandler, server, networkManager);
    309        }
    310
    311        public static void onConnectionClosed(INetworkManager manager, EntityPlayer player)
    312        {
    313            NetworkRegistry.instance().connectionClosed(manager, player);
    314        }
    315
    316
    317        public static void openGui(EntityPlayer player, Object mod, int modGuiId, World world, int x, int y, int z)
    318        {
    319            ModContainer mc = FMLCommonHandler.instance().findContainerFor(mod);
    320            if (mc == null)
    321            {
    322                NetworkModHandler nmh = instance().findNetworkModHandler(mod);
    323                if (nmh != null)
    324                {
    325                    mc = nmh.getContainer();
    326                }
    327                else
    328                {
    329                    FMLLog.warning("A mod tried to open a gui on the server without being a NetworkMod");
    330                    return;
    331                }
    332            }
    333            if (player instanceof EntityPlayerMP)
    334            {
    335                NetworkRegistry.instance().openRemoteGui(mc, (EntityPlayerMP) player, modGuiId, world, x, y, z);
    336            }
    337            else
    338            {
    339                NetworkRegistry.instance().openLocalGui(mc, player, modGuiId, world, x, y, z);
    340            }
    341        }
    342
    343        public static Packet getEntitySpawningPacket(Entity entity)
    344        {
    345            EntityRegistration er = EntityRegistry.instance().lookupModSpawn(entity.getClass(), false);
    346            if (er == null)
    347            {
    348                return null;
    349            }
    350            if (er.usesVanillaSpawning())
    351            {
    352                return null;
    353            }
    354            return PacketDispatcher.getPacket("FML", FMLPacket.makePacket(Type.ENTITYSPAWN, er, entity, instance().findNetworkModHandler(er.getContainer())));
    355        }
    356
    357        public static void makeEntitySpawnAdjustment(int entityId, EntityPlayerMP player, int serverX, int serverY, int serverZ)
    358        {
    359            Packet250CustomPayload pkt = PacketDispatcher.getPacket("FML", FMLPacket.makePacket(Type.ENTITYSPAWNADJUSTMENT, entityId, serverX, serverY, serverZ));
    360            player.playerNetServerHandler.sendPacketToPlayer(pkt);
    361        }
    362
    363        public static InetAddress computeLocalHost() throws IOException
    364        {
    365            InetAddress add = null;
    366            List<InetAddress> addresses = Lists.newArrayList();
    367            InetAddress localHost = InetAddress.getLocalHost();
    368            for (NetworkInterface ni : Collections.list(NetworkInterface.getNetworkInterfaces()))
    369            {
    370                if (!ni.isLoopback() && ni.isUp())
    371                {
    372                    addresses.addAll(Collections.list(ni.getInetAddresses()));
    373                    if (addresses.contains(localHost))
    374                    {
    375                        add = localHost;
    376                        break;
    377                    }
    378                }
    379            }
    380            if (add == null && !addresses.isEmpty())
    381            {
    382                for (InetAddress addr: addresses)
    383                {
    384                    if (addr.getAddress().length == 4)
    385                    {
    386                        add = addr;
    387                        break;
    388                    }
    389                }
    390            }
    391            if (add == null)
    392            {
    393                add = localHost;
    394            }
    395            return add;
    396        }
    397
    398        public static Packet3Chat handleChatMessage(NetHandler handler, Packet3Chat chat)
    399        {
    400            return NetworkRegistry.instance().handleChat(handler, chat);
    401        }
    402
    403        public static void handlePacket131Packet(NetHandler handler, Packet131MapData mapData)
    404        {
    405            if (handler instanceof NetServerHandler || mapData.itemID != Item.map.shiftedIndex)
    406            {
    407                // Server side and not "map" packets are always handled by us
    408                NetworkRegistry.instance().handleTinyPacket(handler, mapData);
    409            }
    410            else
    411            {
    412                // Fallback to the net client handler implementation
    413                FMLCommonHandler.instance().handleTinyPacket(handler, mapData);
    414            }
    415        }
    416
    417        public static int getCompatibilityLevel()
    418        {
    419            return PROTOCOL_VERSION;
    420        }
    421
    422        public static boolean vanillaLoginPacketCompatibility()
    423        {
    424            return FMLCommonHandler.instance().getSidedDelegate().getClientCompatibilityLevel() == 0;
    425        }
    426    }