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