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