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