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}