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 }