001    package cpw.mods.fml.common.network;
002    
003    import java.util.Arrays;
004    import java.util.List;
005    import java.util.Map;
006    import java.util.Set;
007    import java.util.logging.Level;
008    
009    import net.minecraft.server.MinecraftServer;
010    import net.minecraft.src.Container;
011    import net.minecraft.src.EntityPlayer;
012    import net.minecraft.src.EntityPlayerMP;
013    import net.minecraft.src.NetHandler;
014    import net.minecraft.src.NetLoginHandler;
015    import net.minecraft.src.NetServerHandler;
016    import net.minecraft.src.NetworkManager;
017    import net.minecraft.src.Packet131MapData;
018    import net.minecraft.src.Packet1Login;
019    import net.minecraft.src.Packet250CustomPayload;
020    import net.minecraft.src.Packet3Chat;
021    import net.minecraft.src.World;
022    
023    import com.google.common.base.Charsets;
024    import com.google.common.base.Joiner;
025    import com.google.common.base.Splitter;
026    import com.google.common.base.Strings;
027    import com.google.common.collect.ArrayListMultimap;
028    import com.google.common.collect.Iterables;
029    import com.google.common.collect.Lists;
030    import com.google.common.collect.Maps;
031    import com.google.common.collect.Multimap;
032    import com.google.common.collect.Sets;
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.Side;
039    import cpw.mods.fml.common.network.FMLPacket.Type;
040    
041    /**
042     * @author cpw
043     *
044     */
045    public class NetworkRegistry
046    {
047    
048        private static final NetworkRegistry INSTANCE = new NetworkRegistry();
049        /**
050         * A map of active channels per player
051         */
052        private Multimap<Player, String> activeChannels = ArrayListMultimap.create();
053        /**
054         * A map of the packet handlers for packets
055         */
056        private Multimap<String, IPacketHandler> universalPacketHandlers = ArrayListMultimap.create();
057        private Multimap<String, IPacketHandler> clientPacketHandlers = ArrayListMultimap.create();
058        private Multimap<String, IPacketHandler> serverPacketHandlers = ArrayListMultimap.create();
059        /**
060         * A linked set of registered connection handlers
061         */
062        private Set<IConnectionHandler> connectionHandlers = Sets.newLinkedHashSet();
063        private Map<ModContainer, IGuiHandler> serverGuiHandlers = Maps.newHashMap();
064        private Map<ModContainer, IGuiHandler> clientGuiHandlers = Maps.newHashMap();
065        private List<IChatListener> chatListeners = Lists.newArrayList();
066    
067        public static NetworkRegistry instance()
068        {
069            return INSTANCE;
070        }
071        /**
072         * Get the packet 250 channel registration string
073         * @return
074         */
075        byte[] getPacketRegistry(Side side)
076        {
077            return Joiner.on('\0').join(Iterables.concat(Arrays.asList("FML"),universalPacketHandlers.keySet(), side.isClient() ? clientPacketHandlers.keySet() : serverPacketHandlers.keySet())).getBytes(Charsets.UTF_8);
078        }
079        /**
080         * Is the specified channel active for the player?
081         * @param channel
082         * @param player
083         * @return
084         */
085        public boolean isChannelActive(String channel, Player player)
086        {
087            return activeChannels.containsEntry(player,channel);
088        }
089        /**
090         * register a channel to a mod
091         * @param container
092         * @param channelName
093         */
094        public void registerChannel(IPacketHandler handler, String channelName)
095        {
096            if (Strings.isNullOrEmpty(channelName) || (channelName!=null && channelName.length()>16))
097            {
098                FMLLog.severe("Invalid channel name '%s' : %s", channelName, Strings.isNullOrEmpty(channelName) ? "Channel name is empty" : "Channel name is too long (16 chars is maximum)");
099                throw new RuntimeException("Channel name is invalid");
100    
101            }
102            universalPacketHandlers.put(channelName, handler);
103        }
104    
105        public void registerChannel(IPacketHandler handler, String channelName, Side side)
106        {
107            if (side == null)
108            {
109                registerChannel(handler, channelName);
110                return;
111            }
112            if (Strings.isNullOrEmpty(channelName) || (channelName!=null && channelName.length()>16))
113            {
114                FMLLog.severe("Invalid channel name '%s' : %s", channelName, Strings.isNullOrEmpty(channelName) ? "Channel name is empty" : "Channel name is too long (16 chars is maximum)");
115                throw new RuntimeException("Channel name is invalid");
116    
117            }
118            if (side.isClient())
119            {
120                clientPacketHandlers.put(channelName, handler);
121            }
122            else
123            {
124                serverPacketHandlers.put(channelName, handler);
125            }
126        }
127        /**
128         * Activate the channel for the player
129         * @param player
130         */
131        void activateChannel(Player player, String channel)
132        {
133            activeChannels.put(player, channel);
134        }
135        /**
136         * Deactivate the channel for the player
137         * @param player
138         * @param channel
139         */
140        void deactivateChannel(Player player, String channel)
141        {
142            activeChannels.remove(player, channel);
143        }
144        /**
145         * Register a connection handler
146         *
147         * @param handler
148         */
149        public void registerConnectionHandler(IConnectionHandler handler)
150        {
151            connectionHandlers.add(handler);
152        }
153    
154        /**
155         * Register a chat listener
156         * @param listener
157         */
158        public void registerChatListener(IChatListener listener)
159        {
160            chatListeners.add(listener);
161        }
162    
163        void playerLoggedIn(EntityPlayerMP player, NetServerHandler netHandler, NetworkManager manager)
164        {
165            generateChannelRegistration(player, netHandler, manager);
166            for (IConnectionHandler handler : connectionHandlers)
167            {
168                handler.playerLoggedIn((Player)player, netHandler, manager);
169            }
170        }
171    
172        String connectionReceived(NetLoginHandler netHandler, NetworkManager manager)
173        {
174            for (IConnectionHandler handler : connectionHandlers)
175            {
176                String kick = handler.connectionReceived(netHandler, manager);
177                if (!Strings.isNullOrEmpty(kick))
178                {
179                    return kick;
180                }
181            }
182            return null;
183        }
184    
185        void connectionOpened(NetHandler netClientHandler, String server, int port, NetworkManager networkManager)
186        {
187            for (IConnectionHandler handler : connectionHandlers)
188            {
189                handler.connectionOpened(netClientHandler, server, port, networkManager);
190            }
191        }
192    
193        void connectionOpened(NetHandler netClientHandler, MinecraftServer server, NetworkManager networkManager)
194        {
195            for (IConnectionHandler handler : connectionHandlers)
196            {
197                handler.connectionOpened(netClientHandler, server, networkManager);
198            }
199        }
200    
201        void clientLoggedIn(NetHandler clientHandler, NetworkManager manager, Packet1Login login)
202        {
203            generateChannelRegistration(clientHandler.getPlayer(), clientHandler, manager);
204            for (IConnectionHandler handler : connectionHandlers)
205            {
206                handler.clientLoggedIn(clientHandler, manager, login);
207            }
208        }
209    
210        void connectionClosed(NetworkManager manager)
211        {
212            for (IConnectionHandler handler : connectionHandlers)
213            {
214                handler.connectionClosed(manager);
215            }
216        }
217    
218        void generateChannelRegistration(EntityPlayer player, NetHandler netHandler, NetworkManager manager)
219        {
220            Packet250CustomPayload pkt = new Packet250CustomPayload();
221            pkt.channel = "REGISTER";
222            pkt.data = getPacketRegistry(player instanceof EntityPlayerMP ? Side.SERVER : Side.CLIENT);
223            pkt.length = pkt.data.length;
224            manager.addToSendQueue(pkt);
225        }
226    
227        void handleCustomPacket(Packet250CustomPayload packet, NetworkManager network, NetHandler handler)
228        {
229            if ("REGISTER".equals(packet.channel))
230            {
231                handleRegistrationPacket(packet, (Player)handler.getPlayer());
232            }
233            else if ("UNREGISTER".equals(packet.channel))
234            {
235                handleUnregistrationPacket(packet, (Player)handler.getPlayer());
236            }
237            else
238            {
239                handlePacket(packet, network, (Player)handler.getPlayer());
240            }
241        }
242    
243    
244        private void handlePacket(Packet250CustomPayload packet, NetworkManager network, Player player)
245        {
246            String channel = packet.channel;
247            for (IPacketHandler handler : Iterables.concat(universalPacketHandlers.get(channel), player instanceof EntityPlayerMP ? serverPacketHandlers.get(channel) : clientPacketHandlers.get(channel)))
248            {
249                handler.onPacketData(network, packet, player);
250            }
251        }
252    
253        private void handleRegistrationPacket(Packet250CustomPayload packet, Player player)
254        {
255            List<String> channels = extractChannelList(packet);
256            for (String channel : channels)
257            {
258                activateChannel(player, channel);
259            }
260        }
261        private void handleUnregistrationPacket(Packet250CustomPayload packet, Player player)
262        {
263            List<String> channels = extractChannelList(packet);
264            for (String channel : channels)
265            {
266                deactivateChannel(player, channel);
267            }
268        }
269        /**
270         * @param packet
271         * @return
272         */
273        private List<String> extractChannelList(Packet250CustomPayload packet)
274        {
275            String request = new String(packet.data, Charsets.UTF_8);
276            List<String> channels = Lists.newArrayList(Splitter.on('\0').split(request));
277            return channels;
278        }
279    
280        public void registerGuiHandler(Object mod, IGuiHandler handler)
281        {
282            ModContainer mc = FMLCommonHandler.instance().findContainerFor(mod);
283            if (mc == null)
284            {
285                mc = Loader.instance().activeModContainer();
286                FMLLog.log(Level.WARNING, "Mod %s attempted to register a gui network handler during a construction phase", mc.getModId());
287            }
288            NetworkModHandler nmh = FMLNetworkHandler.instance().findNetworkModHandler(mc);
289            if (nmh == null)
290            {
291                FMLLog.log(Level.FINE, "The mod %s needs to be a @NetworkMod to register a Networked Gui Handler", mc.getModId());
292            }
293            else
294            {
295                serverGuiHandlers.put(mc, handler);
296            }
297            clientGuiHandlers.put(mc, handler);
298        }
299        void openRemoteGui(ModContainer mc, EntityPlayerMP player, int modGuiId, World world, int x, int y, int z)
300        {
301            IGuiHandler handler = serverGuiHandlers.get(mc);
302            NetworkModHandler nmh = FMLNetworkHandler.instance().findNetworkModHandler(mc);
303            if (handler != null && nmh != null)
304            {
305                Container container = (Container)handler.getServerGuiElement(modGuiId, player, world, x, y, z);
306                if (container != null)
307                {
308                    player.incrementWindowID();
309                    player.closeInventory();
310                    int windowId = player.currentWindowId;
311                    Packet250CustomPayload pkt = new Packet250CustomPayload();
312                    pkt.channel = "FML";
313                    pkt.data = FMLPacket.makePacket(Type.GUIOPEN, windowId, nmh.getNetworkId(), modGuiId, x, y, z);
314                    pkt.length = pkt.data.length;
315                    player.serverForThisPlayer.sendPacketToPlayer(pkt);
316                    player.craftingInventory = container;
317                    player.craftingInventory.windowId = windowId;
318                    player.craftingInventory.addCraftingToCrafters(player);
319                }
320            }
321        }
322        void openLocalGui(ModContainer mc, EntityPlayer player, int modGuiId, World world, int x, int y, int z)
323        {
324            IGuiHandler handler = clientGuiHandlers.get(mc);
325            FMLCommonHandler.instance().showGuiScreen(handler.getClientGuiElement(modGuiId, player, world, x, y, z));
326        }
327        public Packet3Chat handleChat(NetHandler handler, Packet3Chat chat)
328        {
329            Side s = Side.CLIENT;
330            if (handler instanceof NetServerHandler)
331            {
332                s = Side.SERVER;
333            }
334            for (IChatListener listener : chatListeners)
335            {
336                chat = s.isClient() ? listener.clientChat(handler, chat) : listener.serverChat(handler, chat);
337            }
338    
339            return chat;
340        }
341        public void handleTinyPacket(NetHandler handler, Packet131MapData mapData)
342        {
343            NetworkModHandler nmh = FMLNetworkHandler.instance().findNetworkModHandler((int)mapData.itemID);
344            if (nmh == null)
345            {
346                FMLLog.info("Received a tiny packet for network id %d that is not recognised here", mapData.itemID);
347                return;
348            }
349            if (nmh.hasTinyPacketHandler())
350            {
351                nmh.getTinyPacketHandler().handle(handler, mapData);
352            }
353            else
354            {
355                FMLLog.info("Received a tiny packet for a network mod that does not accept tiny packets %s", nmh.getContainer().getModId());
356            }
357        }
358    }