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.Packet1Login;
018    import net.minecraft.src.Packet250CustomPayload;
019    import net.minecraft.src.Packet3Chat;
020    import net.minecraft.src.World;
021    
022    import com.google.common.base.Charsets;
023    import com.google.common.base.Joiner;
024    import com.google.common.base.Splitter;
025    import com.google.common.base.Strings;
026    import com.google.common.collect.ArrayListMultimap;
027    import com.google.common.collect.Iterables;
028    import com.google.common.collect.Lists;
029    import com.google.common.collect.Maps;
030    import com.google.common.collect.Multimap;
031    import com.google.common.collect.Sets;
032    
033    import cpw.mods.fml.common.FMLCommonHandler;
034    import cpw.mods.fml.common.FMLLog;
035    import cpw.mods.fml.common.Loader;
036    import cpw.mods.fml.common.ModContainer;
037    import cpw.mods.fml.common.Side;
038    import cpw.mods.fml.common.network.FMLPacket.Type;
039    
040    /**
041     * @author cpw
042     *
043     */
044    public class NetworkRegistry
045    {
046    
047        private static final NetworkRegistry INSTANCE = new NetworkRegistry();
048        /**
049         * A map of active channels per player
050         */
051        private Multimap<Player, String> activeChannels = ArrayListMultimap.create();
052        /**
053         * A map of the packet handlers for packets
054         */
055        private Multimap<String, IPacketHandler> universalPacketHandlers = ArrayListMultimap.create();
056        private Multimap<String, IPacketHandler> clientPacketHandlers = ArrayListMultimap.create();
057        private Multimap<String, IPacketHandler> serverPacketHandlers = ArrayListMultimap.create();
058        /**
059         * A linked set of registered connection handlers
060         */
061        private Set<IConnectionHandler> connectionHandlers = Sets.newLinkedHashSet();
062        private Map<ModContainer, IGuiHandler> serverGuiHandlers = Maps.newHashMap();
063        private Map<ModContainer, IGuiHandler> clientGuiHandlers = Maps.newHashMap();
064        private List<IChatListener> chatListeners = Lists.newArrayList();
065    
066        public static NetworkRegistry instance()
067        {
068            return INSTANCE;
069        }
070        /**
071         * Get the packet 250 channel registration string
072         * @return
073         */
074        byte[] getPacketRegistry(Side side)
075        {
076            return Joiner.on('\0').join(Iterables.concat(Arrays.asList("FML"),universalPacketHandlers.keySet(), side.isClient() ? clientPacketHandlers.keySet() : serverPacketHandlers.keySet())).getBytes(Charsets.UTF_8);
077        }
078        /**
079         * Is the specified channel active for the player?
080         * @param channel
081         * @param player
082         * @return
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, NetworkManager 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, NetworkManager 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, NetworkManager 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, NetworkManager networkManager)
193        {
194            for (IConnectionHandler handler : connectionHandlers)
195            {
196                handler.connectionOpened(netClientHandler, server, networkManager);
197            }
198        }
199    
200        void clientLoggedIn(NetHandler clientHandler, NetworkManager 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(NetworkManager manager)
210        {
211            for (IConnectionHandler handler : connectionHandlers)
212            {
213                handler.connectionClosed(manager);
214            }
215        }
216    
217        void generateChannelRegistration(EntityPlayer player, NetHandler netHandler, NetworkManager manager)
218        {
219            Packet250CustomPayload pkt = new Packet250CustomPayload();
220            pkt.channel = "REGISTER";
221            pkt.data = getPacketRegistry(player instanceof EntityPlayerMP ? Side.SERVER : Side.CLIENT);
222            pkt.length = pkt.data.length;
223            manager.addToSendQueue(pkt);
224        }
225    
226        void handleCustomPacket(Packet250CustomPayload packet, NetworkManager network, NetHandler handler)
227        {
228            if ("REGISTER".equals(packet.channel))
229            {
230                handleRegistrationPacket(packet, (Player)handler.getPlayer());
231            }
232            else if ("UNREGISTER".equals(packet.channel))
233            {
234                handleUnregistrationPacket(packet, (Player)handler.getPlayer());
235            }
236            else
237            {
238                handlePacket(packet, network, (Player)handler.getPlayer());
239            }
240        }
241    
242    
243        private void handlePacket(Packet250CustomPayload packet, NetworkManager network, Player player)
244        {
245            String channel = packet.channel;
246            for (IPacketHandler handler : Iterables.concat(universalPacketHandlers.get(channel), player instanceof EntityPlayerMP ? serverPacketHandlers.get(channel) : clientPacketHandlers.get(channel)))
247            {
248                handler.onPacketData(network, packet, player);
249            }
250        }
251    
252        private void handleRegistrationPacket(Packet250CustomPayload packet, Player player)
253        {
254            List<String> channels = extractChannelList(packet);
255            for (String channel : channels)
256            {
257                activateChannel(player, channel);
258            }
259        }
260        private void handleUnregistrationPacket(Packet250CustomPayload packet, Player player)
261        {
262            List<String> channels = extractChannelList(packet);
263            for (String channel : channels)
264            {
265                deactivateChannel(player, channel);
266            }
267        }
268        /**
269         * @param packet
270         * @return
271         */
272        private List<String> extractChannelList(Packet250CustomPayload packet)
273        {
274            String request = new String(packet.data, Charsets.UTF_8);
275            List<String> channels = Lists.newArrayList(Splitter.on('\0').split(request));
276            return channels;
277        }
278    
279        public void registerGuiHandler(Object mod, IGuiHandler handler)
280        {
281            ModContainer mc = FMLCommonHandler.instance().findContainerFor(mod);
282            if (mc == null)
283            {
284                mc = Loader.instance().activeModContainer();
285                FMLLog.log(Level.WARNING, "Mod %s attempted to register a gui network handler during a construction phase", mc.getModId());
286            }
287            NetworkModHandler nmh = FMLNetworkHandler.instance().findNetworkModHandler(mc);
288            if (nmh == null)
289            {
290                FMLLog.log(Level.FINE, "The mod %s needs to be a @NetworkMod to register a Networked Gui Handler", mc.getModId());
291            }
292            else
293            {
294                serverGuiHandlers.put(mc, handler);
295            }
296            clientGuiHandlers.put(mc, handler);
297        }
298        void openRemoteGui(ModContainer mc, EntityPlayerMP player, int modGuiId, World world, int x, int y, int z)
299        {
300            IGuiHandler handler = serverGuiHandlers.get(mc);
301            NetworkModHandler nmh = FMLNetworkHandler.instance().findNetworkModHandler(mc);
302            if (handler != null && nmh != null)
303            {
304                Container container = (Container)handler.getServerGuiElement(modGuiId, player, world, x, y, z);
305                if (container != null)
306                {
307                    player.incrementWindowID();
308                    player.closeInventory();
309                    int windowId = player.currentWindowId;
310                    Packet250CustomPayload pkt = new Packet250CustomPayload();
311                    pkt.channel = "FML";
312                    pkt.data = FMLPacket.makePacket(Type.GUIOPEN, windowId, nmh.getNetworkId(), modGuiId, x, y, z);
313                    pkt.length = pkt.data.length;
314                    player.serverForThisPlayer.sendPacketToPlayer(pkt);
315                    player.craftingInventory = container;
316                    player.craftingInventory.windowId = windowId;
317                    player.craftingInventory.addCraftingToCrafters(player);
318                }
319            }
320        }
321        void openLocalGui(ModContainer mc, EntityPlayer player, int modGuiId, World world, int x, int y, int z)
322        {
323            IGuiHandler handler = clientGuiHandlers.get(mc);
324            FMLCommonHandler.instance().showGuiScreen(handler.getClientGuiElement(modGuiId, player, world, x, y, z));
325        }
326        public Packet3Chat handleChat(NetHandler handler, Packet3Chat chat)
327        {
328            Side s = Side.CLIENT;
329            if (handler instanceof NetServerHandler)
330            {
331                s = Side.SERVER;
332            }
333            for (IChatListener listener : chatListeners)
334            {
335                chat = s.isClient() ? listener.clientChat(handler, chat) : listener.serverChat(handler, chat);
336            }
337    
338            return chat;
339        }
340    }