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