001package cpw.mods.fml.common.network;
002
003import java.util.Arrays;
004import java.util.List;
005import java.util.Map;
006import java.util.Set;
007import java.util.logging.Level;
008
009import net.minecraft.entity.player.EntityPlayer;
010import net.minecraft.entity.player.EntityPlayerMP;
011import net.minecraft.inventory.Container;
012import net.minecraft.network.*;
013import net.minecraft.network.packet.*;
014import net.minecraft.server.MinecraftServer;
015import net.minecraft.world.World;
016
017import com.google.common.base.Charsets;
018import com.google.common.base.Joiner;
019import com.google.common.base.Splitter;
020import com.google.common.base.Strings;
021import com.google.common.collect.ArrayListMultimap;
022import com.google.common.collect.Iterables;
023import com.google.common.collect.Lists;
024import com.google.common.collect.Maps;
025import com.google.common.collect.Multimap;
026import com.google.common.collect.Sets;
027
028import cpw.mods.fml.common.FMLCommonHandler;
029import cpw.mods.fml.common.FMLLog;
030import cpw.mods.fml.common.Loader;
031import cpw.mods.fml.common.ModContainer;
032import cpw.mods.fml.common.network.FMLPacket.Type;
033import cpw.mods.fml.relauncher.Side;
034
035/**
036 * @author cpw
037 *
038 */
039public 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 the {@link Packet250CustomPayload} channel registration string
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 handler the packet handler
085     * @param channelName the channel name to register it with
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}