    001/*
    002 * Forge Mod Loader
    003 * Copyright (c) 2012-2013 cpw.
    004 * All rights reserved. This program and the accompanying materials
    005 * are made available under the terms of the GNU Lesser Public License v2.1
    006 * which accompanies this distribution, and is available at
    007 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
    008 *
    009 * Contributors:
    010 *     cpw - implementation
    011 */
    012
    013package cpw.mods.fml.common.network;
    014
    015import java.util.Arrays;
    016import java.util.List;
    017import java.util.Map;
    018import java.util.Set;
    019import java.util.logging.Level;
    020
    021import net.minecraft.entity.player.EntityPlayer;
    022import net.minecraft.entity.player.EntityPlayerMP;
    023import net.minecraft.inventory.Container;
    024import net.minecraft.network.*;
    025import net.minecraft.network.packet.*;
    026import net.minecraft.server.MinecraftServer;
    027import net.minecraft.world.World;
    028
    029import com.google.common.base.Charsets;
    030import com.google.common.base.Joiner;
    031import com.google.common.base.Splitter;
    032import com.google.common.base.Strings;
    033import com.google.common.collect.ArrayListMultimap;
    034import com.google.common.collect.Iterables;
    035import com.google.common.collect.Lists;
    036import com.google.common.collect.Maps;
    037import com.google.common.collect.Multimap;
    038import com.google.common.collect.Sets;
    039
    040import cpw.mods.fml.common.FMLCommonHandler;
    041import cpw.mods.fml.common.FMLLog;
    042import cpw.mods.fml.common.Loader;
    043import cpw.mods.fml.common.ModContainer;
    044import cpw.mods.fml.common.network.FMLPacket.Type;
    045import cpw.mods.fml.relauncher.Side;
    046
    047/**
    048 * @author cpw
    049 *
    050 */
    051public class NetworkRegistry
    052{
    053
    054    private static final NetworkRegistry INSTANCE = new NetworkRegistry();
    055    /**
    056     * A map of active channels per player
    057     */
    058    private Multimap<Player, String> activeChannels = ArrayListMultimap.create();
    059    /**
    060     * A map of the packet handlers for packets
    061     */
    062    private Multimap<String, IPacketHandler> universalPacketHandlers = ArrayListMultimap.create();
    063    private Multimap<String, IPacketHandler> clientPacketHandlers = ArrayListMultimap.create();
    064    private Multimap<String, IPacketHandler> serverPacketHandlers = ArrayListMultimap.create();
    065    /**
    066     * A linked set of registered connection handlers
    067     */
    068    private Set<IConnectionHandler> connectionHandlers = Sets.newLinkedHashSet();
    069    private Map<ModContainer, IGuiHandler> serverGuiHandlers = Maps.newHashMap();
    070    private Map<ModContainer, IGuiHandler> clientGuiHandlers = Maps.newHashMap();
    071    private List<IChatListener> chatListeners = Lists.newArrayList();
    072
    073    public static NetworkRegistry instance()
    074    {
    075        return INSTANCE;
    076    }
    077    /**
    078     * Get the packet 250 channel registration string
    079     * @return the {@link Packet250CustomPayload} channel registration string
    080     */
    081    byte[] getPacketRegistry(Side side)
    082    {
    083        return Joiner.on('\0').join(Iterables.concat(Arrays.asList("FML"),universalPacketHandlers.keySet(), side.isClient() ? clientPacketHandlers.keySet() : serverPacketHandlers.keySet())).getBytes(Charsets.UTF_8);
    084    }
    085    /**
    086     * Is the specified channel active for the player?
    087     * @param channel
    088     * @param player
    089     */
    090    public boolean isChannelActive(String channel, Player player)
    091    {
    092        return activeChannels.containsEntry(player,channel);
    093    }
    094    /**
    095     * register a channel to a mod
    096     * @param handler the packet handler
    097     * @param channelName the channel name to register it with
    098     */
    099    public void registerChannel(IPacketHandler handler, String channelName)
    100    {
    101        if (Strings.isNullOrEmpty(channelName) || (channelName!=null && channelName.length()>16))
    102        {
    103            FMLLog.severe("Invalid channel name '%s' : %s", channelName, Strings.isNullOrEmpty(channelName) ? "Channel name is empty" : "Channel name is too long (16 chars is maximum)");
    104            throw new RuntimeException("Channel name is invalid");
    105
    106        }
    107        universalPacketHandlers.put(channelName, handler);
    108    }
    109
    110    public void registerChannel(IPacketHandler handler, String channelName, Side side)
    111    {
    112        if (side == null)
    113        {
    114            registerChannel(handler, channelName);
    115            return;
    116        }
    117        if (Strings.isNullOrEmpty(channelName) || (channelName!=null && channelName.length()>16))
    118        {
    119            FMLLog.severe("Invalid channel name '%s' : %s", channelName, Strings.isNullOrEmpty(channelName) ? "Channel name is empty" : "Channel name is too long (16 chars is maximum)");
    120            throw new RuntimeException("Channel name is invalid");
    121
    122        }
    123        if (side.isClient())
    124        {
    125            clientPacketHandlers.put(channelName, handler);
    126        }
    127        else
    128        {
    129            serverPacketHandlers.put(channelName, handler);
    130        }
    131    }
    132    /**
    133     * Activate the channel for the player
    134     * @param player
    135     */
    136    void activateChannel(Player player, String channel)
    137    {
    138        activeChannels.put(player, channel);
    139    }
    140    /**
    141     * Deactivate the channel for the player
    142     * @param player
    143     * @param channel
    144     */
    145    void deactivateChannel(Player player, String channel)
    146    {
    147        activeChannels.remove(player, channel);
    148    }
    149    /**
    150     * Register a connection handler
    151     *
    152     * @param handler
    153     */
    154    public void registerConnectionHandler(IConnectionHandler handler)
    155    {
    156        connectionHandlers.add(handler);
    157    }
    158
    159    /**
    160     * Register a chat listener
    161     * @param listener
    162     */
    163    public void registerChatListener(IChatListener listener)
    164    {
    165        chatListeners.add(listener);
    166    }
    167
    168    void playerLoggedIn(EntityPlayerMP player, NetServerHandler netHandler, INetworkManager manager)
    169    {
    170        generateChannelRegistration(player, netHandler, manager);
    171        for (IConnectionHandler handler : connectionHandlers)
    172        {
    173            handler.playerLoggedIn((Player)player, netHandler, manager);
    174        }
    175    }
    176
    177    String connectionReceived(NetLoginHandler netHandler, INetworkManager manager)
    178    {
    179        for (IConnectionHandler handler : connectionHandlers)
    180        {
    181            String kick = handler.connectionReceived(netHandler, manager);
    182            if (!Strings.isNullOrEmpty(kick))
    183            {
    184                return kick;
    185            }
    186        }
    187        return null;
    188    }
    189
    190    void connectionOpened(NetHandler netClientHandler, String server, int port, INetworkManager networkManager)
    191    {
    192        for (IConnectionHandler handler : connectionHandlers)
    193        {
    194            handler.connectionOpened(netClientHandler, server, port, networkManager);
    195        }
    196    }
    197
    198    void connectionOpened(NetHandler netClientHandler, MinecraftServer server, INetworkManager networkManager)
    199    {
    200        for (IConnectionHandler handler : connectionHandlers)
    201        {
    202            handler.connectionOpened(netClientHandler, server, networkManager);
    203        }
    204    }
    205
    206    void clientLoggedIn(NetHandler clientHandler, INetworkManager manager, Packet1Login login)
    207    {
    208        generateChannelRegistration(clientHandler.getPlayer(), clientHandler, manager);
    209        for (IConnectionHandler handler : connectionHandlers)
    210        {
    211            handler.clientLoggedIn(clientHandler, manager, login);
    212        }
    213    }
    214
    215    void connectionClosed(INetworkManager manager, EntityPlayer player)
    216    {
    217        for (IConnectionHandler handler : connectionHandlers)
    218        {
    219            handler.connectionClosed(manager);
    220        }
    221        activeChannels.removeAll(player);
    222    }
    223
    224    void generateChannelRegistration(EntityPlayer player, NetHandler netHandler, INetworkManager manager)
    225    {
    226        Packet250CustomPayload pkt = new Packet250CustomPayload();
    227        pkt.channel = "REGISTER";
    228        pkt.data = getPacketRegistry(player instanceof EntityPlayerMP ? Side.SERVER : Side.CLIENT);
    229        pkt.length = pkt.data.length;
    230        manager.addToSendQueue(pkt);
    231    }
    232
    233    void handleCustomPacket(Packet250CustomPayload packet, INetworkManager network, NetHandler handler)
    234    {
    235        if ("REGISTER".equals(packet.channel))
    236        {
    237            handleRegistrationPacket(packet, (Player)handler.getPlayer());
    238        }
    239        else if ("UNREGISTER".equals(packet.channel))
    240        {
    241            handleUnregistrationPacket(packet, (Player)handler.getPlayer());
    242        }
    243        else
    244        {
    245            handlePacket(packet, network, (Player)handler.getPlayer());
    246        }
    247    }
    248
    249
    250    private void handlePacket(Packet250CustomPayload packet, INetworkManager network, Player player)
    251    {
    252        String channel = packet.channel;
    253        for (IPacketHandler handler : Iterables.concat(universalPacketHandlers.get(channel), player instanceof EntityPlayerMP ? serverPacketHandlers.get(channel) : clientPacketHandlers.get(channel)))
    254        {
    255            handler.onPacketData(network, packet, player);
    256        }
    257    }
    258
    259    private void handleRegistrationPacket(Packet250CustomPayload packet, Player player)
    260    {
    261        List<String> channels = extractChannelList(packet);
    262        for (String channel : channels)
    263        {
    264            activateChannel(player, channel);
    265        }
    266    }
    267    private void handleUnregistrationPacket(Packet250CustomPayload packet, Player player)
    268    {
    269        List<String> channels = extractChannelList(packet);
    270        for (String channel : channels)
    271        {
    272            deactivateChannel(player, channel);
    273        }
    274    }
    275
    276    private List<String> extractChannelList(Packet250CustomPayload packet)
    277    {
    278        String request = new String(packet.data, Charsets.UTF_8);
    279        List<String> channels = Lists.newArrayList(Splitter.on('\0').split(request));
    280        return channels;
    281    }
    282
    283    public void registerGuiHandler(Object mod, IGuiHandler handler)
    284    {
    285        ModContainer mc = FMLCommonHandler.instance().findContainerFor(mod);
    286        if (mc == null)
    287        {
    288            mc = Loader.instance().activeModContainer();
    289            FMLLog.log(Level.WARNING, "Mod %s attempted to register a gui network handler during a construction phase", mc.getModId());
    290        }
    291        NetworkModHandler nmh = FMLNetworkHandler.instance().findNetworkModHandler(mc);
    292        if (nmh == null)
    293        {
    294            FMLLog.log(Level.FINE, "The mod %s needs to be a @NetworkMod to register a Networked Gui Handler", mc.getModId());
    295        }
    296        else
    297        {
    298            serverGuiHandlers.put(mc, handler);
    299        }
    300        clientGuiHandlers.put(mc, handler);
    301    }
    302    void openRemoteGui(ModContainer mc, EntityPlayerMP player, int modGuiId, World world, int x, int y, int z)
    303    {
    304        IGuiHandler handler = serverGuiHandlers.get(mc);
    305        NetworkModHandler nmh = FMLNetworkHandler.instance().findNetworkModHandler(mc);
    306        if (handler != null && nmh != null)
    307        {
    308            Container container = (Container)handler.getServerGuiElement(modGuiId, player, world, x, y, z);
    309            if (container != null)
    310            {
    311                player.incrementWindowID();
    312                player.closeInventory();
    313                int windowId = player.currentWindowId;
    314                Packet250CustomPayload pkt = new Packet250CustomPayload();
    315                pkt.channel = "FML";
    316                pkt.data = FMLPacket.makePacket(Type.GUIOPEN, windowId, nmh.getNetworkId(), modGuiId, x, y, z);
    317                pkt.length = pkt.data.length;
    318                player.playerNetServerHandler.sendPacketToPlayer(pkt);
    319                player.openContainer = container;
    320                player.openContainer.windowId = windowId;
    321                player.openContainer.addCraftingToCrafters(player);
    322            }
    323        }
    324    }
    325    void openLocalGui(ModContainer mc, EntityPlayer player, int modGuiId, World world, int x, int y, int z)
    326    {
    327        IGuiHandler handler = clientGuiHandlers.get(mc);
    328        FMLCommonHandler.instance().showGuiScreen(handler.getClientGuiElement(modGuiId, player, world, x, y, z));
    329    }
    330    public Packet3Chat handleChat(NetHandler handler, Packet3Chat chat)
    331    {
    332        Side s = Side.CLIENT;
    333        if (handler instanceof NetServerHandler)
    334        {
    335            s = Side.SERVER;
    336        }
    337        for (IChatListener listener : chatListeners)
    338        {
    339            chat = s.isClient() ? listener.clientChat(handler, chat) : listener.serverChat(handler, chat);
    340        }
    341
    342        return chat;
    343    }
    344    public void handleTinyPacket(NetHandler handler, Packet131MapData mapData)
    345    {
    346        NetworkModHandler nmh = FMLNetworkHandler.instance().findNetworkModHandler((int)mapData.itemID);
    347        if (nmh == null)
    348        {
    349            FMLLog.info("Received a tiny packet for network id %d that is not recognised here", mapData.itemID);
    350            return;
    351        }
    352        if (nmh.hasTinyPacketHandler())
    353        {
    354            nmh.getTinyPacketHandler().handle(handler, mapData);
    355        }
    356        else
    357        {
    358            FMLLog.info("Received a tiny packet for a network mod that does not accept tiny packets %s", nmh.getContainer().getModId());
    359        }
    360    }
    361}