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