    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}