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