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