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.INetworkManager;
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         */
084        public boolean isChannelActive(String channel, Player player)
085        {
086            return activeChannels.containsEntry(player,channel);
087        }
088        /**
089         * register a channel to a mod
090         * @param container
091         * @param channelName
092         */
093        public void registerChannel(IPacketHandler handler, String channelName)
094        {
095            if (Strings.isNullOrEmpty(channelName) || (channelName!=null && channelName.length()>16))
096            {
097                FMLLog.severe("Invalid channel name '%s' : %s", channelName, Strings.isNullOrEmpty(channelName) ? "Channel name is empty" : "Channel name is too long (16 chars is maximum)");
098                throw new RuntimeException("Channel name is invalid");
099    
100            }
101            universalPacketHandlers.put(channelName, handler);
102        }
103    
104        public void registerChannel(IPacketHandler handler, String channelName, Side side)
105        {
106            if (side == null)
107            {
108                registerChannel(handler, channelName);
109                return;
110            }
111            if (Strings.isNullOrEmpty(channelName) || (channelName!=null && channelName.length()>16))
112            {
113                FMLLog.severe("Invalid channel name '%s' : %s", channelName, Strings.isNullOrEmpty(channelName) ? "Channel name is empty" : "Channel name is too long (16 chars is maximum)");
114                throw new RuntimeException("Channel name is invalid");
115    
116            }
117            if (side.isClient())
118            {
119                clientPacketHandlers.put(channelName, handler);
120            }
121            else
122            {
123                serverPacketHandlers.put(channelName, handler);
124            }
125        }
126        /**
127         * Activate the channel for the player
128         * @param player
129         */
130        void activateChannel(Player player, String channel)
131        {
132            activeChannels.put(player, channel);
133        }
134        /**
135         * Deactivate the channel for the player
136         * @param player
137         * @param channel
138         */
139        void deactivateChannel(Player player, String channel)
140        {
141            activeChannels.remove(player, channel);
142        }
143        /**
144         * Register a connection handler
145         *
146         * @param handler
147         */
148        public void registerConnectionHandler(IConnectionHandler handler)
149        {
150            connectionHandlers.add(handler);
151        }
152    
153        /**
154         * Register a chat listener
155         * @param listener
156         */
157        public void registerChatListener(IChatListener listener)
158        {
159            chatListeners.add(listener);
160        }
161    
162        void playerLoggedIn(EntityPlayerMP player, NetServerHandler netHandler, INetworkManager manager)
163        {
164            generateChannelRegistration(player, netHandler, manager);
165            for (IConnectionHandler handler : connectionHandlers)
166            {
167                handler.playerLoggedIn((Player)player, netHandler, manager);
168            }
169        }
170    
171        String connectionReceived(NetLoginHandler netHandler, INetworkManager manager)
172        {
173            for (IConnectionHandler handler : connectionHandlers)
174            {
175                String kick = handler.connectionReceived(netHandler, manager);
176                if (!Strings.isNullOrEmpty(kick))
177                {
178                    return kick;
179                }
180            }
181            return null;
182        }
183    
184        void connectionOpened(NetHandler netClientHandler, String server, int port, INetworkManager networkManager)
185        {
186            for (IConnectionHandler handler : connectionHandlers)
187            {
188                handler.connectionOpened(netClientHandler, server, port, networkManager);
189            }
190        }
191    
192        void connectionOpened(NetHandler netClientHandler, MinecraftServer server, INetworkManager networkManager)
193        {
194            for (IConnectionHandler handler : connectionHandlers)
195            {
196                handler.connectionOpened(netClientHandler, server, networkManager);
197            }
198        }
199    
200        void clientLoggedIn(NetHandler clientHandler, INetworkManager manager, Packet1Login login)
201        {
202            generateChannelRegistration(clientHandler.getPlayer(), clientHandler, manager);
203            for (IConnectionHandler handler : connectionHandlers)
204            {
205                handler.clientLoggedIn(clientHandler, manager, login);
206            }
207        }
208    
209        void connectionClosed(INetworkManager manager, EntityPlayer player)
210        {
211            for (IConnectionHandler handler : connectionHandlers)
212            {
213                handler.connectionClosed(manager);
214            }
215            activeChannels.removeAll(player);
216        }
217    
218        void generateChannelRegistration(EntityPlayer player, NetHandler netHandler, INetworkManager 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, INetworkManager 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, INetworkManager 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        private List<String> extractChannelList(Packet250CustomPayload packet)
271        {
272            String request = new String(packet.data, Charsets.UTF_8);
273            List<String> channels = Lists.newArrayList(Splitter.on('\0').split(request));
274            return channels;
275        }
276    
277        public void registerGuiHandler(Object mod, IGuiHandler handler)
278        {
279            ModContainer mc = FMLCommonHandler.instance().findContainerFor(mod);
280            if (mc == null)
281            {
282                mc = Loader.instance().activeModContainer();
283                FMLLog.log(Level.WARNING, "Mod %s attempted to register a gui network handler during a construction phase", mc.getModId());
284            }
285            NetworkModHandler nmh = FMLNetworkHandler.instance().findNetworkModHandler(mc);
286            if (nmh == null)
287            {
288                FMLLog.log(Level.FINE, "The mod %s needs to be a @NetworkMod to register a Networked Gui Handler", mc.getModId());
289            }
290            else
291            {
292                serverGuiHandlers.put(mc, handler);
293            }
294            clientGuiHandlers.put(mc, handler);
295        }
296        void openRemoteGui(ModContainer mc, EntityPlayerMP player, int modGuiId, World world, int x, int y, int z)
297        {
298            IGuiHandler handler = serverGuiHandlers.get(mc);
299            NetworkModHandler nmh = FMLNetworkHandler.instance().findNetworkModHandler(mc);
300            if (handler != null && nmh != null)
301            {
302                Container container = (Container)handler.getServerGuiElement(modGuiId, player, world, x, y, z);
303                if (container != null)
304                {
305                    player.incrementWindowID();
306                    player.closeInventory();
307                    int windowId = player.currentWindowId;
308                    Packet250CustomPayload pkt = new Packet250CustomPayload();
309                    pkt.channel = "FML";
310                    pkt.data = FMLPacket.makePacket(Type.GUIOPEN, windowId, nmh.getNetworkId(), modGuiId, x, y, z);
311                    pkt.length = pkt.data.length;
312                    player.playerNetServerHandler.sendPacketToPlayer(pkt);
313                    player.craftingInventory = container;
314                    player.craftingInventory.windowId = windowId;
315                    player.craftingInventory.addCraftingToCrafters(player);
316                }
317            }
318        }
319        void openLocalGui(ModContainer mc, EntityPlayer player, int modGuiId, World world, int x, int y, int z)
320        {
321            IGuiHandler handler = clientGuiHandlers.get(mc);
322            FMLCommonHandler.instance().showGuiScreen(handler.getClientGuiElement(modGuiId, player, world, x, y, z));
323        }
324        public Packet3Chat handleChat(NetHandler handler, Packet3Chat chat)
325        {
326            Side s = Side.CLIENT;
327            if (handler instanceof NetServerHandler)
328            {
329                s = Side.SERVER;
330            }
331            for (IChatListener listener : chatListeners)
332            {
333                chat = s.isClient() ? listener.clientChat(handler, chat) : listener.serverChat(handler, chat);
334            }
335    
336            return chat;
337        }
338        public void handleTinyPacket(NetHandler handler, Packet131MapData mapData)
339        {
340            NetworkModHandler nmh = FMLNetworkHandler.instance().findNetworkModHandler((int)mapData.itemID);
341            if (nmh == null)
342            {
343                FMLLog.info("Received a tiny packet for network id %d that is not recognised here", mapData.itemID);
344                return;
345            }
346            if (nmh.hasTinyPacketHandler())
347            {
348                nmh.getTinyPacketHandler().handle(handler, mapData);
349            }
350            else
351            {
352                FMLLog.info("Received a tiny packet for a network mod that does not accept tiny packets %s", nmh.getContainer().getModId());
353            }
354        }
355    }