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.Loader;
035    import cpw.mods.fml.common.ModContainer;
036    import cpw.mods.fml.common.Side;
037    import cpw.mods.fml.common.network.FMLPacket.Type;
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    
060        public static NetworkRegistry instance()
061        {
062            return INSTANCE;
063        }
064        /**
065         * Get the packet 250 channel registration string
066         * @return
067         */
068        byte[] getPacketRegistry(Side side)
069        {
070            return Joiner.on('\0').join(Iterables.concat(Arrays.asList("FML"),universalPacketHandlers.keySet(), side.isClient() ? clientPacketHandlers.keySet() : serverPacketHandlers.keySet())).getBytes(Charsets.UTF_8);
071        }
072        /**
073         * Is the specified channel active for the player?
074         * @param channel
075         * @param player
076         * @return
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        void playerLoggedIn(EntityPlayerMP player, NetServerHandler netHandler, NetworkManager manager)
148        {
149            generateChannelRegistration(player, netHandler, manager);
150            for (IConnectionHandler handler : connectionHandlers)
151            {
152                handler.playerLoggedIn((Player)player, netHandler, manager);
153            }
154        }
155    
156        String connectionReceived(NetLoginHandler netHandler, NetworkManager manager)
157        {
158            for (IConnectionHandler handler : connectionHandlers)
159            {
160                String kick = handler.connectionReceived(netHandler, manager);
161                if (!Strings.isNullOrEmpty(kick))
162                {
163                    return kick;
164                }
165            }
166            return null;
167        }
168    
169        void connectionOpened(NetHandler netClientHandler, String server, int port, NetworkManager networkManager)
170        {
171            for (IConnectionHandler handler : connectionHandlers)
172            {
173                handler.connectionOpened(netClientHandler, server, port, networkManager);
174            }
175        }
176    
177        void connectionOpened(NetHandler netClientHandler, MinecraftServer server, NetworkManager networkManager)
178        {
179            for (IConnectionHandler handler : connectionHandlers)
180            {
181                handler.connectionOpened(netClientHandler, server, networkManager);
182            }
183        }
184    
185        void clientLoggedIn(NetHandler clientHandler, NetworkManager manager, Packet1Login login)
186        {
187            generateChannelRegistration(clientHandler.getPlayer(), clientHandler, manager);
188            for (IConnectionHandler handler : connectionHandlers)
189            {
190                handler.clientLoggedIn(clientHandler, manager, login);
191            }
192        }
193    
194        void connectionClosed(NetworkManager manager)
195        {
196            for (IConnectionHandler handler : connectionHandlers)
197            {
198                handler.connectionClosed(manager);
199            }
200        }
201    
202        void generateChannelRegistration(EntityPlayer player, NetHandler netHandler, NetworkManager manager)
203        {
204            Packet250CustomPayload pkt = new Packet250CustomPayload();
205            pkt.channel = "REGISTER";
206            pkt.data = getPacketRegistry(player instanceof EntityPlayerMP ? Side.SERVER : Side.CLIENT);
207            pkt.length = pkt.data.length;
208            manager.addToSendQueue(pkt);
209        }
210    
211        void handleCustomPacket(Packet250CustomPayload packet, NetworkManager network, NetHandler handler)
212        {
213            if ("REGISTER".equals(packet.channel))
214            {
215                handleRegistrationPacket(packet, (Player)handler.getPlayer());
216            }
217            else if ("UNREGISTER".equals(packet.channel))
218            {
219                handleUnregistrationPacket(packet, (Player)handler.getPlayer());
220            }
221            else
222            {
223                handlePacket(packet, network, (Player)handler.getPlayer());
224            }
225        }
226    
227    
228        private void handlePacket(Packet250CustomPayload packet, NetworkManager network, Player player)
229        {
230            String channel = packet.channel;
231            for (IPacketHandler handler : Iterables.concat(universalPacketHandlers.get(channel), player instanceof EntityPlayerMP ? serverPacketHandlers.get(channel) : clientPacketHandlers.get(channel)))
232            {
233                handler.onPacketData(network, packet, player);
234            }
235        }
236    
237        private void handleRegistrationPacket(Packet250CustomPayload packet, Player player)
238        {
239            List<String> channels = extractChannelList(packet);
240            for (String channel : channels)
241            {
242                activateChannel(player, channel);
243            }
244        }
245        private void handleUnregistrationPacket(Packet250CustomPayload packet, Player player)
246        {
247            List<String> channels = extractChannelList(packet);
248            for (String channel : channels)
249            {
250                deactivateChannel(player, channel);
251            }
252        }
253        /**
254         * @param packet
255         * @return
256         */
257        private List<String> extractChannelList(Packet250CustomPayload packet)
258        {
259            String request = new String(packet.data, Charsets.UTF_8);
260            List<String> channels = Lists.newArrayList(Splitter.on('\0').split(request));
261            return channels;
262        }
263    
264        public void registerGuiHandler(Object mod, IGuiHandler handler)
265        {
266            ModContainer mc = FMLCommonHandler.instance().findContainerFor(mod);
267            if (mc == null)
268            {
269                mc = Loader.instance().activeModContainer();
270                FMLLog.log(Level.WARNING, "Mod %s attempted to register a gui network handler during a construction phase", mc.getModId());
271            }
272            NetworkModHandler nmh = FMLNetworkHandler.instance().findNetworkModHandler(mc);
273            if (nmh == null)
274            {
275                FMLLog.log(Level.FINE, "The mod %s needs to be a @NetworkMod to register a Networked Gui Handler", mc.getModId());
276            }
277            else
278            {
279                serverGuiHandlers.put(mc, handler);
280            }
281            clientGuiHandlers.put(mc, handler);
282        }
283        void openRemoteGui(ModContainer mc, EntityPlayerMP player, int modGuiId, World world, int x, int y, int z)
284        {
285            IGuiHandler handler = serverGuiHandlers.get(mc);
286            NetworkModHandler nmh = FMLNetworkHandler.instance().findNetworkModHandler(mc);
287            if (handler != null && nmh != null)
288            {
289                Container container = (Container)handler.getServerGuiElement(modGuiId, player, world, x, y, z);
290                if (container != null)
291                {
292                    player.incrementWindowID();
293                    player.closeInventory();
294                    int windowId = player.currentWindowId;
295                    Packet250CustomPayload pkt = new Packet250CustomPayload();
296                    pkt.channel = "FML";
297                    pkt.data = FMLPacket.makePacket(Type.GUIOPEN, windowId, nmh.getNetworkId(), modGuiId, x, y, z);
298                    pkt.length = pkt.data.length;
299                    player.serverForThisPlayer.sendPacketToPlayer(pkt);
300                    player.craftingInventory = container;
301                    player.craftingInventory.windowId = windowId;
302                    player.craftingInventory.addCraftingToCrafters(player);
303                }
304            }
305        }
306        void openLocalGui(ModContainer mc, EntityPlayer player, int modGuiId, World world, int x, int y, int z)
307        {
308            IGuiHandler handler = clientGuiHandlers.get(mc);
309            FMLCommonHandler.instance().showGuiScreen(handler.getClientGuiElement(modGuiId, player, world, x, y, z));
310        }
311    }