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